pax_global_header00006660000000000000000000000064150677612420014524gustar00rootroot0000000000000052 comment=4c1f02a43f993758d445952ccd96e552752defec bpftrace-0.24.1/000077500000000000000000000000001506776124200133765ustar00rootroot00000000000000bpftrace-0.24.1/.clang-format000066400000000000000000000053021506776124200157510ustar00rootroot00000000000000--- 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 InsertNewlineAtEOF: true 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 --- Language: ObjC DisableFormat: true ... bpftrace-0.24.1/.clang-format-ignore000066400000000000000000000000551506776124200172320ustar00rootroot00000000000000tests/data/data_source.c tests/testprogs/*.c bpftrace-0.24.1/.clang-tidy000066400000000000000000000030361506776124200154340ustar00rootroot00000000000000Checks: - -* - bugprone-suspicious-memset-usage - bugprone-unchecked-optional-access - bugprone-undefined-memory-manipulation - bugprone-unused-raii - bugprone-use-after-move - google-readability-casting - misc-misleading-identifier - misc-misplaced-const - misc-non-copyable-objects - misc-throw-by-value-catch-by-reference - misc-redundant-expression - misc-unconventional-assign-operator - misc-static-assert - modernize-* - -modernize-avoid-c-arrays - -modernize-use-nodiscard - -modernize-use-integer-sign-comparison - -modernize-use-trailing-return-type - performance-unnecessary-copy-initialization - readability-* - -readability-avoid-nested-conditional-operator - -readability-braces-around-statements - -readability-convert-member-functions-to-static - -readability-else-after-return - -readability-enum-initial-value - -readability-function-cognitive-complexity - -readability-function-size - -readability-identifier-length - -readability-implicit-bool-conversion - -readability-isolate-declaration - -readability-magic-numbers - -readability-make-member-function-const - -readability-suspicious-call-argument CheckOptions: # gcc and llvm did not introduce when supporting C++20, so using # std::format is temporarily not allowed. # see issue https://github.com/bpftrace/bpftrace/issues/4185 modernize-use-std-format.ReplacementFormat: "DISALLOW" WarningsAsErrors: "*" UseColor: true HeaderFilterRegex: ".*" ExcludeHeaderFilterRegex: ".*/(parser\\.tab|location)\\.hh" bpftrace-0.24.1/.devcontainer/000077500000000000000000000000001506776124200161355ustar00rootroot00000000000000bpftrace-0.24.1/.devcontainer/Dockerfile000066400000000000000000000007671506776124200201410ustar00rootroot00000000000000FROM mcr.microsoft.com/vscode/devcontainers/base:bullseye # Required nix install dependencies. RUN apt-get update && apt-get install -y curl bzip2 adduser direnv vim RUN su - vscode -c 'sh <(curl -L https://releases.nixos.org/nix/nix-2.26.3/install) --no-daemon' RUN su - vscode -c 'mkdir -p ~/.config/nix && echo "extra-experimental-features = nix-command flakes" > ~/.config/nix/nix.conf' RUN su - vscode -c 'nix-env -iA nixpkgs.direnv' RUN su - vscode -c 'direnv hook bash >> ~/.bashrc' USER vscode bpftrace-0.24.1/.devcontainer/devcontainer.json000066400000000000000000000004431506776124200215120ustar00rootroot00000000000000{ "build": { "dockerfile": "Dockerfile" }, "customizations": { "vscode": { "extensions": [ "mkhl.direnv", "llvm-vs-code-extensions.vscode-clangd", "ms-vscode.cmake-tools" ] } }, "postStartCommand": "nix develop -c true && direnv allow" } bpftrace-0.24.1/.editorconfig000066400000000000000000000003231506776124200160510ustar00rootroot00000000000000[*] 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.24.1/.envrc000066400000000000000000000000121506776124200145050ustar00rootroot00000000000000use flake bpftrace-0.24.1/.git-blame-ignore-revs000066400000000000000000000001221506776124200174710ustar00rootroot00000000000000# Move opening brace to starting context 453198bdabdaffdc1cb600038af60644e1326656 bpftrace-0.24.1/.gitattributes000066400000000000000000000001171506776124200162700ustar00rootroot00000000000000*.bt linguist-language=D *.bt linguist-vendored *.ll linguist-detectable=false bpftrace-0.24.1/.github/000077500000000000000000000000001506776124200147365ustar00rootroot00000000000000bpftrace-0.24.1/.github/CODEOWNERS000066400000000000000000000001651506776124200163330ustar00rootroot00000000000000# Request reviews automatically to speed up the PR workflow * @ajor @viktormalik @danobi @fbs @jordalgo @amscanne bpftrace-0.24.1/.github/FUNDING.yml000066400000000000000000000000321506776124200165460ustar00rootroot00000000000000open_collective: bpftrace bpftrace-0.24.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001506776124200171215ustar00rootroot00000000000000bpftrace-0.24.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000005271506776124200216170ustar00rootroot00000000000000--- 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.24.1/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002601506776124200211070ustar00rootroot00000000000000blank_issues_enabled: true contact_links: - name: Community Support url: https://github.com/bpftrace/bpftrace/discussions about: Please ask and answer questions here bpftrace-0.24.1/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000004111506776124200226420ustar00rootroot00000000000000--- 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.24.1/.github/ISSUE_TEMPLATE/release.md000066400000000000000000000034711506776124200210700ustar00rootroot00000000000000--- name: Release tracker about: Create a tracker issue for the upcoming release. Should be used by maintainers only. --- ### Release progress - [ ] Create release branch `release/v0..x` () - [ ] Add support for LLVM - [ ] Bump `MAX_LLVM_MAJOR` in [CMakeLists.txt](https://github.com/bpftrace/bpftrace/blob/master/CMakeLists.txt) - [ ] Add new Nix target in [flake.nix](https://github.com/bpftrace/bpftrace/blob/master/flake.nix) - [ ] Add CI job to [.github/workflows/ci.yml](https://github.com/bpftrace/bpftrace/blob/master/.github/workflows/ci.yml) - [ ] Update LLVM in Nixpkgs to .1.0 - [ ] **Release bpftrace 0..0 ()** - [ ] Mark the release in [CHANGELOG.md](https://github.com/bpftrace/bpftrace/blob/master/CHANGELOG.md) - [ ] Update `bpftrace_VERSION_*` in [CMakeLists.txt](https://github.com/bpftrace/bpftrace/blob/master/CMakeLists.txt) - [ ] Draft a new release in GitHub - [ ] Update the docs on the bpftrace website. [Instructions](https://github.com/bpftrace/website?#updating-the-docs). - [ ] Forward-port [CHANGELOG.md](https://github.com/bpftrace/bpftrace/blob/master/CHANGELOG.md) and [CMakeLists.txt](https://github.com/bpftrace/bpftrace/blob/master/CMakeLists.txt) changes to the master branch. See [Release Process](https://github.com/bpftrace/bpftrace/blob/master/docs/release_process.md) for general information on the release process. bpftrace-0.24.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000011161506776124200205360ustar00rootroot00000000000000 ##### Checklist - [ ] Language changes are updated in `docs/language.md`, `docs/stdlib.md`, or `man/adoc/bpftrace.adoc` - [ ] User-visible and non-trivial changes updated in `CHANGELOG.md` - [ ] The new behaviour is covered by tests bpftrace-0.24.1/.github/actions/000077500000000000000000000000001506776124200163765ustar00rootroot00000000000000bpftrace-0.24.1/.github/actions/configure_kvm/000077500000000000000000000000001506776124200212345ustar00rootroot00000000000000bpftrace-0.24.1/.github/actions/configure_kvm/action.yml000066400000000000000000000012431506776124200232340ustar00rootroot00000000000000name: 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.24.1/.github/codeql/000077500000000000000000000000001506776124200162055ustar00rootroot00000000000000bpftrace-0.24.1/.github/codeql/codeql-config.yml000066400000000000000000000000651506776124200214430ustar00rootroot00000000000000paths-ignore: - tests/runtime/engine/cmake_vars.py bpftrace-0.24.1/.github/include/000077500000000000000000000000001506776124200163615ustar00rootroot00000000000000bpftrace-0.24.1/.github/include/aot_allow.txt000066400000000000000000000143111506776124200211030ustar00rootroot00000000000000aot.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.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_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.24.1/.github/include/ci.py000077500000000000000000000303061506776124200173330ustar00rootroot00000000000000#!/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, Self, 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") class FoldOutput: """ GitHub Actions output folding context manager. Will automatically fold output for all operations. In an ideal world we'd like to leave the failed operations unfolded, but there's currently no way to do this in GHA without buffering output. """ def __init__(self, name: str): self.name = name self.in_ci = truthy(CI) def __enter__(self) -> Self: if self.in_ci: # Start a collapsible section in GitHub Actions logs print(f"::group::{self.name}") else: print(f"\n======= {self.name} ======") return self def __exit__(self, exc_type, exc_val, exc_tb): if self.in_ci: print("::endgroup::") 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) if not env: env = {} # Nix needs to know the home dir if "HOME" in os.environ: env["HOME"] = os.environ["HOME"] c.append("--command") if as_root: 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", "--preserve-env=" + ",".join([n for n in env]), ] c += cmd 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""" with FoldOutput("configure"): # 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", f"-DHARDENED_STDLIB=1", ] # fmt: on shell(c) def build(): """Build everything""" with FoldOutput("build"): cpus = multiprocessing.cpu_count() shell(["make", "-C", BUILD_DIR, "-j", str(cpus)], env={"AFL_USE_ASAN": "1"}) 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(): try: with FoldOutput(name): fn() except subprocess.CalledProcessError: 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 fuzz(): """ Run a basic fuzz smoke test. """ # Make basic inputs and output directories. Path(BUILD_DIR, "inputs").mkdir(exist_ok=True) Path(BUILD_DIR, "outputs").mkdir(exist_ok=True) # For now, seed the inputs directly with a trivial program. These can be # codified differently in the future, but this is sufficient for a basic # fuzz smoke test. Actual fuzzing should have a wide variety of inputs. Path(BUILD_DIR, "inputs", "seed.bt").write_text("BEGIN {}") results = [ test_one( "fuzz", lambda: truthy(RUN_TESTS), lambda: shell( # fmt: off cmd = [ "afl-fuzz", "-M", "0", "-m", "none", "-i", "inputs", "-o", "outputs", "-E", "10", # 10 execs, smoke test only. "-t", "60000", "--", "src/bpftrace", "--test=codegen", "@@", ], env = { "AFL_NO_AFFINITY": "1", "ASAN_OPTIONS": "abort_on_error=1,symbolize=0", "BPFTRACE_BTF": "", "BPFTRACE_MAX_AST_NODES": "200", "BPFTRACE_AVAILABLE_FUNCTIONS_TEST": "", # This setting [1] is used to skip the core pattern check, # so crashes may be missed. Since this is just a smoke # test, we use this rather than change the system state. # [1] https://github.com/mirrorer/afl/blob/master/docs/env_variables.txt "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES": "", }, cwd=Path(BUILD_DIR), # fmt: on ), ), ] tests_finish(results) 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() if CC.startswith("afl-"): fuzz() else: test() if __name__ == "__main__": main() bpftrace-0.24.1/.github/include/static.sh000077500000000000000000000017051506776124200202120ustar00rootroot00000000000000#!/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.24.1/.github/workflows/000077500000000000000000000000001506776124200167735ustar00rootroot00000000000000bpftrace-0.24.1/.github/workflows/binary.yml000066400000000000000000000016401506776124200210030ustar00rootroot00000000000000# 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@v17 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v2 - 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.24.1/.github/workflows/ci.yml000066400000000000000000000076231506776124200201210ustar00rootroot00000000000000name: CI on: push: branches: [ master, release/* ] pull_request: paths-ignore: - "man/**" - "docs/**" - "**/*.md" # 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@v17 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v2 - name: clang-format run: nix develop --command git clang-format --diff origin/master --extensions cpp,h 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 TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: LLVM 17 CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm17 TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: LLVM 18 CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm18 TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: LLVM 19 CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm19 TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: LLVM 20 CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm20 TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: LLVM 21 Release CMAKE_BUILD_TYPE: Release NIX_TARGET: .#bpftrace-llvm21 TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: LLVM 21 Debug CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm21 TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: LLVM 21 Clang Debug CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm21 CC: clang CXX: clang++ TOOLS_TEST_OLDVERSION: runqlen.bt - NAME: Fuzzing CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-fuzz CC: afl-clang-fast CXX: afl-clang-fast++ - NAME: Latest kernel (LLVM 21 Debug) CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm21 NIX_TARGET_KERNEL: .#kernel-6_14 TOOLS_TEST_OLDVERSION: runqlen.bt # Need host 6.14 kernel - NAME: AOT (LLVM 21 Debug) CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm21 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@v17 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v2 - 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.24.1/.github/workflows/codeql.yml000066400000000000000000000032771506776124200207760ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ master, release/* ] pull_request: 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@v17 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v2 - 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@v3 with: config-file: ./.github/codeql/codeql-config.yml languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild uses: github/codeql-action/autobuild@v3 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@v3 with: category: "/language:${{ matrix.language }}" bpftrace-0.24.1/.github/workflows/distros.yml000066400000000000000000000011251506776124200212040ustar00rootroot00000000000000name: Distros on: push: branches: [ master, release/* ] workflow_dispatch: # 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.24.1/.github/workflows/flakehub.yml000066400000000000000000000015631506776124200213040ustar00rootroot00000000000000# 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@v17 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v2 - uses: DeterminateSystems/flakehub-push@v5 with: visibility: public name: bpftrace/bpftrace tag: ${{ inputs.tag }} include-output-paths: true bpftrace-0.24.1/.github/workflows/issue-metrics.yml000066400000000000000000000035471506776124200223230ustar00rootroot00000000000000name: Generate Issue and PR Metrics on: workflow_dispatch: permissions: contents: read jobs: build: name: issue pr metrics runs-on: ubuntu-latest permissions: issues: write pull-requests: read steps: - name: Get dates for last month shell: bash run: | # Calculate the first day of the previous month first_day=$(date -d "last month" +%Y-%m-01) # Calculate the last day of the previous month last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d) # Get the month name month_name=$(date -d "last month" +%B) #Set environment variables with the date range and month name echo "$first_day..$last_day" echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV" echo "month_name=$month_name" >> "$GITHUB_ENV" - name: Run issue-metrics tool for issues uses: github/issue-metrics@v3 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SEARCH_QUERY: 'repo:bpftrace/bpftrace is:issue created:${{ env.last_month }} -reason:"not planned"' HIDE_ASSIGNEE: true HIDE_TIME_TO_ANSWER: true REPORT_TITLE: Issue Metrics for ${{ env.month_name }} OUTPUT_FILE: issue_metrics.md - name: Run issue-metrics tool for PRs uses: github/issue-metrics@v3 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SEARCH_QUERY: 'repo:bpftrace/bpftrace is:pr created:${{ env.last_month }} -reason:"not planned"' HIDE_ASSIGNEE: true HIDE_TIME_TO_ANSWER: true REPORT_TITLE: PR Metrics for ${{ env.month_name }} OUTPUT_FILE: pr_metrics.md - name: Generate metrics report shell: bash run: | cat ./issue_metrics.md >> $GITHUB_STEP_SUMMARY cat ./pr_metrics.md >> $GITHUB_STEP_SUMMARY bpftrace-0.24.1/.github/workflows/release.yml000066400000000000000000000014251506776124200211400ustar00rootroot00000000000000# 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@v17 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v2 - 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.24.1/.github/workflows/static.yml000066400000000000000000000006321506776124200210060ustar00rootroot00000000000000name: Static on: push: branches: [ master, release/* ] 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.24.1/.github/workflows/stdlib-docs.yml000066400000000000000000000020541506776124200217260ustar00rootroot00000000000000name: Standard Library Documentation Check on: push: branches: [ master, release/* ] pull_request: paths: - "src/stdlib/**/*.bt" - "scripts/generate_stdlib_docs.py" # 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: check-stdlib-docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.x' - name: Generate stdlib documentation run: ./scripts/generate_stdlib_docs.py - name: Check for changes in docs/stdlib.md run: | if git diff --exit-code docs/stdlib.md; then echo "✅ docs/stdlib.md is up to date" else echo "⌠docs/stdlib.md is out of date. Please run './scripts/generate_stdlib_docs.py src/stdlib/base.bt' and commit the changes." echo "Changes detected:" git diff docs/stdlib.md exit 1 fi bpftrace-0.24.1/.github/workflows/tidy.yml000066400000000000000000000012511506776124200204660ustar00rootroot00000000000000name: "Clang Tidy" on: [ 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: 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@v17 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v2 - name: Run clang-tidy run: ./scripts/clang_tidy.sh bpftrace-0.24.1/.gitignore000066400000000000000000000002771506776124200153740ustar00rootroot00000000000000/.idea build/ build-*/ tests/runtime/*.pyc .direnv .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.24.1/.vscode/000077500000000000000000000000001506776124200147375ustar00rootroot00000000000000bpftrace-0.24.1/.vscode/settings.json000066400000000000000000000004101506776124200174650ustar00rootroot00000000000000{ "cmake.buildDirectory": "${workspaceFolder}/build-vscode", "clangd.arguments": [ "-background-index", "-compile-commands-dir=${workspaceFolder}/build-vscode" ], "editor.formatOnSave": true, "C_Cpp.clang_format_style": "file" }bpftrace-0.24.1/CHANGELOG.md000066400000000000000000003760771506776124200152330ustar00rootroot00000000000000# 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). ## [0.24.1] 2025-10-03 #### Fixed - Fix missing location.hh build error on Alpine - [#4635](https://github.com/bpftrace/bpftrace/pull/4635) - Fix build with gcc-16 - [#4614](https://github.com/bpftrace/bpftrace/pull/4614) - ast: Avoid undefined behavior in literal folding - [#4649](https://github.com/bpftrace/bpftrace/pull/4649) - Fix missing map key buffers for map aggregate functions - [#4670](https://github.com/bpftrace/bpftrace/pull/4670) - Fix fentry/fexit recursion check - [#4684](https://github.com/bpftrace/bpftrace/pull/4684) #### Docs - Fix docs for PERCPU map types - [#4646](https://github.com/bpftrace/bpftrace/pull/4646) ## [0.24.0] 2025-09-17 #### Breaking Changes - Drop majority of DWARF support. Only uprobe argument parsing remains. - [#3921](https://github.com/bpftrace/bpftrace/pull/3921) - [#3950](https://github.com/bpftrace/bpftrace/pull/3950) - Removed config option 'symbol_source' - it no longer has any effect - [#3925](https://github.com/bpftrace/bpftrace/pull/3925) - Rawtracepoints now require kernel BTF - [#3944](https://github.com/bpftrace/bpftrace/pull/3944) - Ustack and kstack symbols are automatically enhanced with debug info if available - [#3999](https://github.com/bpftrace/bpftrace/pull/3999) - Error by default if any probe fails to attach - [#4097](https://github.com/bpftrace/bpftrace/pull/4097) - Require BPF_MAP_TYPE_RINGBUF to be available - [#3974](https://github.com/bpftrace/bpftrace/pull/3974) - Require kernel uprobe ref counting to be available for USDTs with semaphores - [#4199](https://github.com/bpftrace/bpftrace/pull/4199) - `strcontains` and `has_key` now return boolean values instead of 1 and 0 - [#4280](https://github.com/bpftrace/bpftrace/pull/4280) - JSON serialization for unsupported and unknown types (e.g. `float`) may now use `null` now rather than the empty string - [#4302](https://github.com/bpftrace/bpftrace/pull/4302) - Text mode now emits all non-script generated output (e.g., errors, attached notifications) to `stderr` instead of `stdout` - [#4504](https://github.com/bpftrace/bpftrace/pull/4504) #### Added - Add ncpus builtin to get the number of CPUs. - [#4105](https://github.com/bpftrace/bpftrace/pull/4105) - Use blazesym for user space address symbolization - [#3884](https://github.com/bpftrace/bpftrace/pull/3884) - Add simple block expressions - [#3780](https://github.com/bpftrace/bpftrace/pull/3780) - Add map declaration syntax (behind an "unstable" config flag) - [#3863](https://github.com/bpftrace/bpftrace/pull/3863) - Add license config to specify BPF license - [#3905](https://github.com/bpftrace/bpftrace/pull/3905) - Rawtracepoints can now use `args` builtin and list params - [#3918](https://github.com/bpftrace/bpftrace/pull/3918) - Add ability to specify rawtracepoint modules - [#3944](https://github.com/bpftrace/bpftrace/pull/3944) - Add 'show_debug_info' config for blazesym - [#3999](https://github.com/bpftrace/bpftrace/pull/3999) - Add hygienic macros (behind an "unstable" config flag) - [#4037](https://github.com/bpftrace/bpftrace/pull/4037) - Add warning when unset or empty positional parameters are used - [#4095](https://github.com/bpftrace/bpftrace/pull/4095) - Support accessing up to 255 USDT probe args - [#4118](https://github.com/bpftrace/bpftrace/pull/4118) - Add new builtin "usermode" - [#4129](https://github.com/bpftrace/bpftrace/pull/4129) - Warn on discarded return values for specific builtin functions - [#4191](https://github.com/bpftrace/bpftrace/pull/4191) - For loops now support `..` ranges - [#4126](https://github.com/bpftrace/bpftrace/pull/4126) - For loops now support `break` and `continue` - [#4250](https://github.com/bpftrace/bpftrace/pull/4250) - Add `pid` and `tid` functions for choosing between the initial or the current namespace - [#4254](https://github.com/bpftrace/bpftrace/pull/4254) - Add boolean values (`true` and `false`) - [#4280](https://github.com/bpftrace/bpftrace/pull/4280) - Add duration literals - suffixes include: `ns`, `us`, `ms`, `s`, `m`, `h`, `d` - [#4317](https://github.com/bpftrace/bpftrace/pull/4317) - Add `getopt` function to handle named command line program arguments - [#4258](https://github.com/bpftrace/bpftrace/pull/4258) - Add `socket_cookie` function to get the cookie of a socket - [#4345](https://github.com/bpftrace/bpftrace/pull/4345) - Introduce `tseries` for capturing time series data - [#3838](https://github.com/bpftrace/bpftrace/pull/3838) - Add ability to attach to running BPF programs and sub-programs via `fentry:bpf:prog_name` or `fentry:bpf:prog_id:prog_name` - [#4354](https://github.com/bpftrace/bpftrace/pull/4354) - Add `BENCH` for creating microbenchmarks - [#4343](https://github.com/bpftrace/bpftrace/pull/4343) - Add 'assert' and 'ppid' to the macro standard library - [#4366](https://github.com/bpftrace/bpftrace/pull/4366) - Add 'errorf' for printing error messages with the source location - [#4414](https://github.com/bpftrace/bpftrace/pull/4414) - Add ability to call most builtins with call-style syntax e.g. `comm()` - [#4420](https://github.com/bpftrace/bpftrace/pull/4420) - Allow non-constant array access - [#4491](https://github.com/bpftrace/bpftrace/pull/4491) - Add '0' flag for printf to fill with leading 0s - [#4510](https://github.com/bpftrace/bpftrace/pull/4510) - Add support for LLVM 21 - [#4528](https://github.com/bpftrace/bpftrace/pull/4528) #### Changed - kprobe: support verbose mode listing - [#4362](https://github.com/bpftrace/bpftrace/pull/4362) - `-p` CLI flag now applies to all probes (except BEGIN/END) - [#3800](https://github.com/bpftrace/bpftrace/pull/3800) - Introduce automatic session probes - [#3772](https://github.com/bpftrace/bpftrace/pull/3772) - Positional params can be used in any part of a probe string - [#3956](https://github.com/bpftrace/bpftrace/pull/3956) - Add signed type checking for map assignments - [#4132](https://github.com/bpftrace/bpftrace/pull/4132) - Add signed type checking for map keys - [#4136](https://github.com/bpftrace/bpftrace/pull/4136) - `delete` now returns 1 if successful, 0 if not - [#4186](https://github.com/bpftrace/bpftrace/pull/4186) - if `delete` fails it will only print a warning if return value is not handled - [#4186](https://github.com/bpftrace/bpftrace/pull/4186) - Change "Attaching N probes..." to "Attached N probes" - [#4194](https://github.com/bpftrace/bpftrace/pull/4194) - runqlat.bt: ignore idle task - [#4194](https://github.com/bpftrace/bpftrace/pull/4291) - Only cache symbols from targeted process - [#4315](https://github.com/bpftrace/bpftrace/pull/4315) - Automatically exit if only BEGIN/END probes are specified - [#4358](https://github.com/bpftrace/bpftrace/pull/4358) - Moved docs for builtins, functions, and map functions into stdlib.md and moved language docs into language.md - [#4367](https://github.com/bpftrace/bpftrace/pull/4367) - Make probe provider names case insensitive - [#4371](https://github.com/bpftrace/bpftrace/pull/4371) #### Deprecated #### Removed #### Fixed - Fix build failures due to missing location.hh - [#3987](https://github.com/bpftrace/bpftrace/pull/3987) - Fix 32-bit build failures due to missing cast - [#4006](https://github.com/bpftrace/bpftrace/pull/4006) - Don't crash if kernel isn't built with PID namespaces - [#3976](https://github.com/bpftrace/bpftrace/pull/3976) - Allow sized_type idents to be used for field access - [#4064](https://github.com/bpftrace/bpftrace/pull/4064) - Fix per-cpu map update warning - [#4047](https://github.com/bpftrace/bpftrace/pull/4074) - Fix probe firing order for fexit and software - [#4113](https://github.com/bpftrace/bpftrace/pull/4113) - Fix type mismatch error for map assignments - [#4130](https://github.com/bpftrace/bpftrace/pull/4130) - Parse BTF for implicit kernel modules in kprobe/kretprobe - [#4137](https://github.com/bpftrace/bpftrace/pull/4137) - Fix execution watchpoints - [#4139](https://github.com/bpftrace/bpftrace/pull/4139) - Fix incorrect reporting of attached count for multi probes - [#4194](https://github.com/bpftrace/bpftrace/pull/4194) - Attach watchpoint probes to newly created threads - [#4239](https://github.com/bpftrace/bpftrace/pull/4239) - Fix reading of __data_loc tracepoint arguments - [#4238](https://github.com/bpftrace/bpftrace/pull/4238) - Fix kstack/ustack on big-endian systems - [#4244](https://github.com/bpftrace/bpftrace/pull/4244) - Report expanded probe name when trying to attach - [#4353](https://github.com/bpftrace/bpftrace/pull/4353) - Fix codegen optimization bug with the modulo operator - [#4383](https://github.com/bpftrace/bpftrace/pull/4383) #### Security #### Docs #### Tools - opensnoop.bt: support openat2 syscall - [#4310](https://github.com/bpftrace/bpftrace/pull/4310) - killsnoop.bt: display signal name instead of value - [#4234](https://github.com/bpftrace/bpftrace/pull/4234) - killsnoop.bt: support tkill() and tgkill() - [#4190](https://github.com/bpftrace/bpftrace/pull/4190) - Remove tools example usage txt files and move info to comments - [#4187](https://github.com/bpftrace/bpftrace/pull/4187) - Fix biosnoop.bt to print comm from block_io_start probe - [#4013](https://github.com/bpftrace/bpftrace/pull/4013) ## [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 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.24.1/CMakeLists.txt000066400000000000000000000202721506776124200161410ustar00rootroot00000000000000cmake_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 24) set(bpftrace_VERSION_PATCH 1) include(GNUInstallDirs) set(WARNINGS_AS_ERRORS OFF CACHE BOOL "Build with -Werror") set(HARDENED_STDLIB OFF CACHE BOOL "Enable hardened definitions for standard library (for development use only)") 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(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(HARDENED_STDLIB) add_compile_definitions("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG") add_compile_definitions("-D_GLIBCXX_ASSERTIONS") if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") # _FORTIFY_SOURCE requires at least -O1 add_compile_definitions("-D_FORTIFY_SOURCE=3") endif() endif() 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}/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} -Wcounterexamples") else() set(BISON_FLAGS "-Dparser_class_name={Parser} -Wcounterexamples") 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) find_package(LibDw) 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() # 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 21) 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}) string(REGEX REPLACE "-rc[0-9]+" "" CLANG_VERSION ${LLVM_PACKAGE_VERSION}) find_package(Clang ${CLANG_VERSION} REQUIRED) include_directories(SYSTEM ${CLANG_INCLUDE_DIRS}) # 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(LIBDW_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBDW) 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.24.1/CONTRIBUTING-TOOLS.md000066400000000000000000000011611506776124200165240ustar00rootroot00000000000000# 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.24.1/CONTRIBUTING.md000066400000000000000000000101231506776124200156240ustar00rootroot00000000000000# 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 = { unstable_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.24.1/GOVERNANCE.md000066400000000000000000000236251506776124200153570ustar00rootroot00000000000000# Governance This document describes the governance model for bpftrace. It is intended to be a living document that is updated as the project evolves. ## Overview bpftrace is a meritocratic, consensus-based community project. Anyone with an interest in the project can join the community, contribute to the project design and participate in the decision making process. This document describes how that participation takes place and how to set about earning merit within the bpftrace community. ## Roles and responsibilities ### Users Users are community members who have a need for the project. They are the most important members of the community and without them the project would have no purpose. Anyone can be a user; there are no special requirements. The project asks its users to participate in the project and community as much as possible. User contributions enable the project team to ensure that they are satisfying the needs of those users. Common user contributions include (but are not limited to): - evangelising about the project (e.g. a link on a website and word-of-mouth awareness raising) - informing developers of strengths and weaknesses from a new user perspective - providing moral support (a ‘thank you’ goes a long way) Users who continue to engage with the project and its community will often become more and more involved. Such users may find themselves becoming contributors, as described in the next section. ### Contributors Contributors are community members who contribute in concrete ways to the project. Anyone can become a contributor. There is no expectation of commitment to the project, no specific skill requirements and no selection process. In addition to their actions as users, contributors may also find themselves doing one or more of the following: - supporting new users (existing users are often the best people to support new users) - reporting bugs - answering/responding to issues and discussions - identifying requirements - coding - assisting with project infrastructure (e.g. Github CI) - writing documentation - fixing bugs - adding features - facilitating distrubtion (e.g. packaging for distributions) Contributors primarily engage with the project through the mechanisms in Github. They submit changes to the project itself via Github Pull Requests, which will be considered for inclusion in the project by existing maintainers and committers (see sections below). The most appropriate place to ask for help when making a first contribution is on the issue itself; there is a curated [list of "good first issues"](https://github.com/bpftrace/bpftrace/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22). As contributors gain experience and familiarity with the project, their profile within, and commitment to, the community will increase. At some stage, they may find themselves being nominated for committership. ### Committers Committers are community members who have shown that they are committed to the continued development of the project through ongoing engagement with the community. Committership allows contributors to more easily carry on with their project related activities by giving them write access, meaning they can merge approved PRs themselves. This does not mean that a committer is free to do what they want. In fact, committers have no more authority over the project than contributors. While committership indicates a valued member of the community who has demonstrated a healthy respect for the project’s aims and objectives, their work continues to be reviewed by the maintainers before acceptance. The key difference between a committer and a contributor is when this approval is sought from the community. A committer seeks approval after the contribution is made, rather than before. Seeking approval after making a contribution is known as a commit-then-review process. It is more efficient to allow trusted people to make direct contributions, as the majority of those contributions will be accepted by the project. The project employs various communication mechanisms to ensure that all contributions are reviewed by the community as a whole. By the time a contributor is invited to become a committer, they will have become familiar with the project’s various tools as a user and then as a contributor. Anyone can become a committer; there are no special requirements, other than to have shown a willingness and ability to participate in the project as a team player. Typically, a potential committer will need to show that they have an understanding of the project, its objectives and its strategy. They will also have provided valuable contributions to the project over a period of time. New committers can be nominated by any existing maintainer (see below). Once they have been nominated, there will be an informal majority among the maintainers. It is important to recognise that commitership is a privilege, not a right. That privilege must be earned and once earned it can be removed by the maintainers in extreme circumstances. However, under normal circumstances committership exists for as long as the committer wishes to continue engaging with the project. A committer who shows an above-average level of contribution to the project, particularly with respect to its strategic direction and long-term health, may be nominated to become a maintainer. This role is described below. ### Maintainers The maintainers of bpftrace consist of those individuals identified as [‘code owners’](https://github.com/bpftrace/bpftrace/blob/master/.github/CODEOWNERS). Maintainers have additional responsibilities over and above those of a committer. These responsibilities ensure the smooth running of the project. Maintainers are expected to: - Review PRs. Since maintainer approval is required for any changes, the project's velocity hinges on speedy turnaround times for reviews. We also don’t want to discourage new contributors by having their PRs sit in Github purgatory. - Attend monthly office hours. - Respond to discussions, questions, and issues. Maintainers have a broader context than most and being responsive also keeps bpftrace users from getting discouraged. - Mentor and teach new developers. Guide these folks on first issues. - Participate in planning and larger design and project decisions. - Help triage and groom the issue backlog. Maintainers vote on new committers and new maintainers. They make decisions when community consensus cannot be reached. In addition, at least one approval from a maintainer is required for each submitted Pull Request. To become a maintainer, you have to first be a committer. Any maintainer can nominate a committer for maintainership. Once they have been nominated, there will be an informal majority vote amongst maintainers only over email (see details below about voting). ## Support All participants in the community are encouraged to provide support for new users within the project management infrastructure. This support is provided as a way of growing the community. Those seeking support should recognise that all support activity within the project is voluntary and is therefore provided as and when time allows. There are currently no mechanisms set up for users requiring guaranteed response times or results. ## Contribution process Anyone can contribute to the project, regardless of their skills. First time contributors should look through the [list of "good first issues"](https://github.com/bpftrace/bpftrace/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) and ask questions on the issue(s) they are interested in picking up. Here is [a more detailed guide on contributing](CONTRIBUTING.md). ## Decision making process Decisions about the future of the project are made through discussion with all members of the community, from the newest user to the most experienced maintainer. All non-sensitive project management discussion takes place on the bpftrace Github project, often times in the [issues](https://github.com/bpftrace/bpftrace/issues) themselves. Occasionally, sensitive discussion occurs offline among the maintainers. ### Merging Code At least one approval from a maintainer is required for each submitted Pull Request; this also applies to maintainers in all cases where the changes are non-trivial. If there is a disagreement among maintainers (or changes requested despite an approval from another maintainer), please allow time for the maintainers to work out the issue amongst themselves or do your best to answer/address the concerns of the dissenting maintainer. ### Proposals and RFCS Any community member can make a proposal or RFC for consideration by the community via [this guide](CONTRIBUTING.md#rfc-process). In general, as long as nobody explicitly opposes a proposal or patch, it is recognised as having the support of the community. This is called lazy consensus - that is, those who have not stated their opinion explicitly have implicitly agreed to the implementation of the proposal. ### Voting Formal voting only occurs when the consensus-based process has been fully exhausted. Meaning if maintainers can't come to an agreement, there will be a vote amongst the maintainers. However, every member of the community is encouraged to express their opinions in all discussions, RFCs, and strategies. A simple majority wins the vote. If there is no majority the proposal or decision is rejected. Maintainers have 10 days to vote otherwise their vote is forfeit. ## Conclusion A clear and transparent governance document is a key part of any open development project. It defines the rules of engagement within the community and describes what level of influence a community member can expect to have over a project. In addition, it enables members to decide their level of involvement with that community. In the case of a meritocracy, it also provides a clear way to contribute and a highly visible reward system. Most of the language in this document was taken from [OSS Watch](http://oss-watch.ac.uk/resources/meritocraticgovernancemodel). bpftrace-0.24.1/INSTALL.md000066400000000000000000000234431506776124200150340ustar00rootroot00000000000000# 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 & Clang 10.0+ development packages - LibElf - LibDw - 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) bpftrace-0.24.1/LICENSE000066400000000000000000000261361506776124200144130ustar00rootroot00000000000000 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.24.1/README.md000066400000000000000000000113001506776124200146500ustar00rootroot00000000000000

bpftrace

[![Build Status](https://github.com/bpftrace/bpftrace/actions/workflows/ci.yml/badge.svg)](https://github.com/bpftrace/bpftrace/actions/workflows/ci.yml) [![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) - [Tutorial](docs/tutorial_one_liners.md) - [The bpftrace Language](docs/language.md) - [Standard Library](docs/stdlib.md) - [CLI Manual](man/adoc/bpftrace.adoc) - [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.24.1/cmake/000077500000000000000000000000001506776124200144565ustar00rootroot00000000000000bpftrace-0.24.1/cmake/BuildBPF.cmake000066400000000000000000000141741506776124200170560ustar00rootroot00000000000000# Functions to build intermediate BPF modules. # # The `bpf` function will produce bitcode, an object as well as the BTF type # information for a given file. find_program(GCC gcc REQUIRED) find_program(BPFTOOL bpftool REQUIRED) find_program(PAHOLE pahole REQUIRED) find_program(NM nm REQUIRED) find_program(AWK awk REQUIRED) find_program(LLVM_OBJCOPY NAMES llvm-objcopy llvm-objcopy-${LLVM_VERSION_MAJOR} llvm${LLVM_VERSION_MAJOR}-objcopy REQUIRED) find_program(CLANG NAMES clang-${LLVM_VERSION_MAJOR} REQUIRED) function(bpf NAME) cmake_parse_arguments( ARG "" "BITCODE;OBJECT;BINARY;BTF;SOURCE;BTF_BASE;FUNCTIONS" "DEPENDS" ${ARGN} ) if (NOT DEFINED ARG_SOURCE) set(ARG_SOURCE "${NAME}.c") endif () if (NOT DEFINED ARG_OBJECT) set(ARG_OBJECT "${NAME}.o") endif () add_custom_target(${NAME}) # The joys of BPF & debug tooling 101. # # The purpose for this function is to generate various types of output for a # given source file, for testing and embedding purposes: # * LLVM bitcode file compatible with our major version of LLVM. # * A binary annotated with DWARF type information. # * Encoded BTF type information for this source unit. # * A list of functions defined in the file. # # Naturally, you'd assume that this could be implementated mostly as a chain # of steps, save the intermediate results along the way. Wrong! Unfortunately, # each of these steps has their own happy path. # # First, the bitcode. This is actually straight-forward! We need to ensure # that this gets built with the correct version of LLVM (not whatever # compiler CMAKE has selected) so that it is compatible with the LLVM # libraries that we are linking against. We can even correctly select bpf as # our target, to get the extent that there is any early specialization. # # Next, you might assume that taking this bitcode and turning it into a BPF # object is the logical thing to do, in order to ensure that all the types # are compatible and consistent. In fact, this even *seems* to work. But # after banging your head on the keyboard for a while, you'll realize that # something is broken. As you contort clang, and try building from scratch, # you'll realize that clang seems to generate proper annotations and # relocations for loadable programs (the happy path for that!), but fails to # generate the same types for base types. In fact, in doesn't even generate # named arguments without an explicit `-O2`. Oh well, I suppose that the # common path for the kernel is to build with `gcc` and then rely on the # `pahole` conversion. Let's do that. # # But hold on a second, we said that we wanted to build a BPF target? Turns # out that `gcc` can't do that very well. So `clang` can't build our BPF # target because of incorrect BTF, and `gcc` can't build the BPF target. So # we need to build the source natively, use `pahole` to generate the BTF # data, and then extract that separately. This makes some sense, as it is the # kernel happy path. # # But at least what we built above has the DWARF data, right? Wrong! If we # were to link the full binary, too much gets stripped out to generate the # full set of BTF information. So we need to generate just the object, add # BTF information, and separately link that single object into a binary to # have the DWARF data in a ELF binary that we're looking for. # # Finally, we can breath a sight of relief and generate our list of # functions. Like the bitcode, this works as expected. if (DEFINED ARG_BITCODE) add_custom_command( OUTPUT ${ARG_BITCODE} DEPENDS ${ARG_SOURCE} ${ARG_DEPENDS} COMMAND ${CLANG} -emit-llvm -g -target bpf -D__TARGET_ARCH_x86 -I ${CMAKE_CURRENT_BINARY_DIR} -c ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_SOURCE} -o ${CMAKE_CURRENT_BINARY_DIR}/${ARG_BITCODE} VERBATIM ) add_custom_target(${NAME}_gen_bitcode DEPENDS ${ARG_BITCODE}) add_dependencies(${NAME} ${NAME}_gen_bitcode) endif() add_custom_command( OUTPUT ${ARG_OBJECT} DEPENDS ${ARG_SOURCE} ${ARG_DEPENDS} # See above: fresh compilation and the use of `gcc`. COMMAND ${GCC} -Wno-attributes -g -I ${CMAKE_CURRENT_BINARY_DIR} -c ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_SOURCE} -o ${CMAKE_CURRENT_BINARY_DIR}/${ARG_OBJECT} COMMAND cmake -E env LLVM_OBJCOPY=${LLVM_OBJCOPY} ${PAHOLE} -J ${CMAKE_CURRENT_BINARY_DIR}/${ARG_OBJECT} VERBATIM ) # You should never depend on the output of a custom command more than once. # Therefore, we define an intermediate target for the object, and depend on # this instead. See [1]. # [1] https://discourse.cmake.org/t/how-to-avoid-parallel-build-race-conditions/727 add_custom_target( ${NAME}_gen_object DEPENDS ${ARG_OBJECT} ) add_dependencies(${NAME} ${NAME}_gen_object) if (DEFINED ARG_BINARY) add_custom_command( OUTPUT ${ARG_BINARY} DEPENDS ${ARG_SOURCE} ${NAME}_gen_object COMMAND ${GCC} -g -o ${CMAKE_CURRENT_BINARY_DIR}/${ARG_BINARY} ${CMAKE_CURRENT_BINARY_DIR}/${ARG_OBJECT} COMMAND cmake -E env LLVM_OBJCOPY=${LLVM_OBJCOPY} ${PAHOLE} -J ${CMAKE_CURRENT_BINARY_DIR}/${ARG_BINARY} VERBATIM ) add_custom_target(${NAME}_gen_binary DEPENDS ${ARG_BINARY}) add_dependencies(${NAME} ${NAME}_gen_binary) endif() if (DEFINED ARG_BTF) add_custom_command( OUTPUT ${ARG_BTF} DEPENDS ${ARG_SOURCE} ${NAME}_gen_object # See above re: the use of the `gcc`-compiled object, ignoring the binary. COMMAND ${LLVM_OBJCOPY} --dump-section .BTF=${CMAKE_CURRENT_BINARY_DIR}/${ARG_BTF} ${CMAKE_CURRENT_BINARY_DIR}/${ARG_OBJECT} VERBATIM ) add_custom_target(${NAME}_gen_btf DEPENDS ${ARG_BTF}) add_dependencies(${NAME} ${NAME}_gen_btf) endif () if (DEFINED ARG_FUNCTIONS) add_custom_command( OUTPUT ${ARG_FUNCTIONS} DEPENDS ${ARG_SOURCE} ${NAME}_gen_object COMMAND ${NM} ${CMAKE_CURRENT_BINARY_DIR}/${ARG_OBJECT} | ${AWK} -v ORS=\\\\n "$2 == \"T\" { print $3 }" > ${CMAKE_CURRENT_BINARY_DIR}/${ARG_FUNCTIONS} VERBATIM ) add_custom_target(${NAME}_gen_functions DEPENDS ${ARG_FUNCTIONS}) add_dependencies(${NAME} ${NAME}_gen_functions) endif () endfunction() bpftrace-0.24.1/cmake/CmakeUninstall.cmake.in000066400000000000000000000017501506776124200210020ustar00rootroot00000000000000# 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}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove $ENV{DESTDIR}${file} OUTPUT_VARIABLE rm_out RESULT_VARIABLE 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.24.1/cmake/Embed.cmake000066400000000000000000000021771506776124200165030ustar00rootroot00000000000000# Functions to embed source files. find_program(XXD xxd REQUIRED) function(embed NAME SOURCE) cmake_parse_arguments( ARG "" "OUTPUT;HEX;VAR" "DEPENDS" ${ARGN} ) if (NOT DEFINED ARG_HEX) set(ARG_HEX "${NAME}.hex") endif () if (NOT DEFINED ARG_VAR) set(ARG_VAR "${NAME}") endif () if (NOT DEFINED ARG_OUTPUT) set(ARG_OUTPUT "${NAME}.h") endif () add_custom_command( OUTPUT ${ARG_HEX} COMMAND ${XXD} -i < ${SOURCE} > ${ARG_HEX} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${SOURCE} ${ARG_DEPENDS} VERBATIM ) add_custom_command( OUTPUT ${ARG_OUTPUT} COMMAND ${CMAKE_COMMAND} -DSOURCE=${CMAKE_CURRENT_BINARY_DIR}/${ARG_HEX} -DVAR=${ARG_VAR} -DNAMESPACE=${ARG_NAMESPACE} -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/${ARG_OUTPUT} -DTEMPLATE=${CMAKE_SOURCE_DIR}/cmake/Embed.tmpl -P ${CMAKE_SOURCE_DIR}/cmake/EmbedRun.cmake DEPENDS ${ARG_HEX} ${CMAKE_SOURCE_DIR}/cmake/EmbedRun.cmake ${CMAKE_SOURCE_DIR}/cmake/Embed.tmpl VERBATIM ) add_custom_target( ${NAME} DEPENDS ${ARG_OUTPUT} ) endfunction() bpftrace-0.24.1/cmake/Embed.tmpl000066400000000000000000000001531506776124200163670ustar00rootroot00000000000000#pragma once namespace ${NAMESPACE} { constexpr unsigned char ${VAR}[] = { ${DATA} }; } // ${NAMESPACE} bpftrace-0.24.1/cmake/EmbedRun.cmake000066400000000000000000000004221506776124200171570ustar00rootroot00000000000000# 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 ${SOURCE} DATA) configure_file(${TEMPLATE} ${OUTPUT}) bpftrace-0.24.1/cmake/FindGMock.cmake000066400000000000000000000107451506776124200172700ustar00rootroot00000000000000# 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.24.1/cmake/FindLibBcc.cmake000066400000000000000000000055301506776124200174020ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibBfd.cmake000066400000000000000000000063401506776124200174060ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibBlazesym.cmake000066400000000000000000000012551506776124200205010ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibBpf.cmake000066400000000000000000000043231506776124200174210ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibBz2.cmake000066400000000000000000000006421506776124200173470ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibCereal.cmake000066400000000000000000000007051506776124200201050ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibDw.cmake000066400000000000000000000017121506776124200172630ustar00rootroot00000000000000# - Try to find libdw # Once done this will define # # LIBDW_FOUND - system has libdw # LIBDW_INCLUDE_DIRS - the libdw include directory # LIBDW_LIBRARIES - Link these to use libdw # LIBDW_DEFINITIONS - Compiler switches required for using libdw # if (LIBDW_LIBRARIES AND LIBDW_INCLUDE_DIRS) set (LibDw_FIND_QUIETLY TRUE) endif (LIBDW_LIBRARIES AND LIBDW_INCLUDE_DIRS) find_path (LIBDW_INCLUDE_DIRS NAMES libdw.h libdwfl.h PATH_SUFFIXES elfutils libdw PATHS ENV CPATH) find_library (LIBDW_LIBRARIES NAMES dw PATH_SUFFIXES elfutils libdw PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBDW_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibDw "Please install the libdw development package" LIBDW_LIBRARIES LIBDW_INCLUDE_DIRS) mark_as_advanced(LIBDW_INCLUDE_DIRS LIBDW_LIBRARIES) bpftrace-0.24.1/cmake/FindLibEbl.cmake000066400000000000000000000007161506776124200174160ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibElf.cmake000066400000000000000000000022271506776124200174210ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibLzma.cmake000066400000000000000000000006551506776124200176210ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibOpcodes.cmake000066400000000000000000000017461506776124200203140ustar00rootroot00000000000000# - Try to find libopcodes # Once done this will define # # LIBOPCODES_FOUND - system has libopcodes # LIBOPCODES_INCLUDE_DIRS - the libopcodes include directory # LIBOPCODES_LIBRARIES - Link these to use libopcodes # LIBOPCODES_DEFINITIONS - Compiler switches required for using libopcodes #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.24.1/cmake/FindLibPcap.cmake000066400000000000000000000016001506776124200175700ustar00rootroot00000000000000# - 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.24.1/cmake/FindLibZ.cmake000066400000000000000000000007651506776124200171310ustar00rootroot00000000000000# - 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.24.1/cmake/Util.cmake000066400000000000000000000010741506776124200163770ustar00rootroot00000000000000# 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.24.1/cmake/Version.cmake000066400000000000000000000021131506776124200171020ustar00rootroot00000000000000# 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.24.1/docker/000077500000000000000000000000001506776124200146455ustar00rootroot00000000000000bpftrace-0.24.1/docker/Dockerfile.debian000066400000000000000000000015641506776124200200660ustar00rootroot00000000000000# 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 \ bpftool \ binutils-dev \ bison \ build-essential \ clang \ cmake \ flex \ libbpf-dev \ libbpfcc-dev \ libcereal-dev \ libdw-dev \ libelf-dev \ libiberty-dev \ libpcap-dev \ llvm-dev \ libclang-dev \ libclang-cpp-dev \ pahole \ systemtap-sdt-dev \ xxd \ 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.24.1/docker/Dockerfile.fedora000066400000000000000000000016451506776124200201040ustar00rootroot00000000000000# 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 \ awk \ asciidoctor \ bcc-devel \ bison \ binutils-devel \ bpftool \ cereal-devel \ clang-devel \ cmake \ elfutils-devel \ elfutils-libelf-devel \ elfutils-libs \ flex \ gcc \ gcc-c++ \ libpcap-devel \ libbpf-devel \ llvm-devel \ make \ pahole \ systemtap-sdt-devel \ xxd \ 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.24.1/docker/Dockerfile.static000066400000000000000000000024021506776124200201230ustar00rootroot00000000000000# This Dockerfile is used to test STATIC_LINKING=ON builds in the CI FROM alpine:3.22 RUN apk add --update \ asciidoctor \ argp-standalone \ bash \ bcc-dev \ bcc-static \ binutils-dev \ bison \ blazesym-dev \ blazesym-static \ bpftool \ 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.24.1/docker/Dockerfile.ubuntu000066400000000000000000000016001506776124200201550ustar00rootroot00000000000000# 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 \ libdw-dev \ libelf-dev \ libiberty-dev \ libpcap-dev \ llvm-dev \ libclang-dev \ libclang-cpp-dev \ linux-tools-common \ pahole \ systemtap-sdt-dev \ xxd \ 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.24.1/docs/000077500000000000000000000000001506776124200143265ustar00rootroot00000000000000bpftrace-0.24.1/docs/coding_guidelines.md000066400000000000000000000102131506776124200203200ustar00rootroot00000000000000# 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.24.1/docs/dependency_support.md000066400000000000000000000036221506776124200205650ustar00rootroot00000000000000# 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 **Minimum kernel version: 5.15** The linux kernel is bpftrace's biggest runtime dependency. While we would like to support the oldest LTS kernel version, we also want to make sure bpftrace is keeping up with the latest features. As a compromise bpftrace supports stable kernels and the 4 most recent LTS kernels. Users that require support on older kernels can often simply use [older versions of bpftrace](https://github.com/bpftrace/bpftrace/releases). The source of truth on EOL dates and LTS kernels is https://www.kernel.org/. 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 or detailed error message. ## 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.24.1/docs/design_principles.md000066400000000000000000000076161506776124200203630ustar00rootroot00000000000000# 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.24.1/docs/developers.md000066400000000000000000000175271506776124200170340ustar00rootroot00000000000000# 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). ## Performance We aim to not be wasteful, but always keep in mind that performance of the BPF programs and runtime are the things in the critical path. Often, simplicity and understandability on non-critical paths is often more important than performance. That said, occasionally it is useful to measure the performance of different parts of the pipeline. You may run bpftrace using `--test-mode compiler-bench` in order to see the performance of the various passes during compilation. Similarly, you may benchmark the code generated by bpftrace using `BENCH` probes and `--test-mode bench`: ``` $ bpftrace --test-mode bench -e 'BENCH:my_benchmark { @a++; }' Attached 1 probe +--------------+--------------+ | BENCHMARK | AVERAGE TIME | +--------------+--------------+ | my_benchmark | 270ns | +--------------+--------------+ ``` ## 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.24.1/docs/fuzzing.md000066400000000000000000000110151506776124200163420ustar00rootroot00000000000000# 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. This is not always efficient for large programs, but still can find many bugs. 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. ## Options ### `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 Before starting, it is highly recommended to read the [AFL][AFL] or [AFLPlusPlus][AFL++] documentation. ### Setup The fuzzing setup relies on `nix`. See the [nix instructions](./nix.md). ### Compile To use AFL, we need to compile the program with the AFL compiler. If you are using `nix`, you would enter a development shell and build as follows: ``` nix develop #.bpftrace-fuzz CC=afl-clang-fast CXX=afl-clang-fast++ cmake -B build-fuzz -DCMAKE_BUILD_TYPE=Debug -DBUILD_ASAN=1 ``` then, in order to build the tree: ``` cd build-fuzz && AFL_USE_ASAN=1 make -j$(nproc) ``` Note that the address sanitizer might take a lot of memory. If you want to fuzz without it, please remove `AFL_USE_ASAN` and `-DBUILD_ASAN`. ### Running AFL recommends 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, fuzz away! AFL and the address sanitizer have a lot of settings, so please read documentation for the details. The currently recommended way to run the fuzzer is by using the `--test=codegen` mode and providing overrides: ``` AFL_NO_AFFINITY=1 \ ASAN_OPTIONS=abort_on_error=1,symbolize=0 \ BPFTRACE_BTF= \ BPFTRACE_MAX_AST_NODES=200 \ BPFTRACE_AVAILABLE_FUNCTIONS_TEST= \ afl-fuzz -a text -M 0 -m none -i ./input -o ./output -t 3000 -- \ src/bpftrace --test=codegen @@ 2>/dev/null ``` In the above, `-i` specifics the input directory, and `-o` specifies 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 using sample `bpftrace` programs from the source directory, tests or other locations. If some inputs that cause a program crash is found, `output/crashes` contains them. The timeout for each execution (in milliseconds) is provided by `-t`. Finally, '@@' will be replaced by the input file generated by the fuzzer. ## 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) [AFL]: https://github.com/google/AFL [AFL++]: https://github.com/AFLplusplus/AFLplusplus bpftrace-0.24.1/docs/internals_development.md000066400000000000000000000676571506776124200212760ustar00rootroot00000000000000# 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.24.1/docs/language.md000066400000000000000000001763361506776124200164530ustar00rootroot00000000000000# The bpftrace 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 [Preamble](#preamble) and one or more [Action Blocks](#action-blocks). ``` preamble actionblock1 actionblock2 ``` ## Action Blocks Each action block consists of three parts: ``` probe[,probe] /predicate/ { action } ``` * **Probes**\ A probe specifies the event and event type to attach to. [Probes list](#probes). * **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 [Probes](#probes) section for details on the available probe types. ## Arguments This refers to traced function and tracepoint arguments. There are two ways to access these arguments and the way you choose depends on the [probe type](#probes). - For `uprobe`, `kprobe`, and `usdt` use the `argN` format. - For `rawtracepoint`, `tracepoint`, `fentry`, `fexit`, and `uprobe` (with DWARF) use the `args` format. ### argN These keywords allow access to the nth argument passed to the function being traced. For the first argument use `arg0`, for the second `arg1`, and so forth. The type of each arg is an `int64` and will often require casting to non scalar types, e.g., `$x = (struct qstr *)arg1`. These are extracted from the CPU registers. The amount of args passed in registers depends on the CPU architecture. ### args This keyword represents the struct of all arguments of the traced function. You can print the entire structure via `print(args)` or access particular fields using the dot syntax, e.g., `$x = str(args.filename);`. To see the args for a particular function, you can use [verbose listing mode](../man/adoc/bpftrace.adoc#listing-probes). Example: ``` # bpftrace -lv 'fentry:tcp_reset' fentry:tcp_reset struct sock * sk struct sk_buff * skb ``` ## 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]); } ``` ## Command Line Parameters Custom options can be passed to a bpftrace program itself via positional or named parameters. It is recommended to use **named parameters** because they are less ambiguous in terms of their types and meaning (they have actual names). ### Named Parameters Named parameters can be accessed in a bpftrace program via the `getopt` function call, e.g., `getopt("my_named_param", 5)`, `getopt("my_bool_param")`. The first argument is the parameter name and the second is the default value when that argument is not passed on the command line. If the second argument is not provided it indicates that the parameter is a `boolean` type. Named parameters must come AFTER a double dash (`--`) when being passed on the command line, e.g., ``` # bpftrace -e 'begin { print((getopt("aa", 1), getopt("bb"))); }' -- --aa=20 --bb ``` Here `getopt("aa", 1)` would evaluate to `20` and `getopt("bb")` would evaluate to `true`. Named parameters require the `=` to set their value unless they are boolean parameters (like 'bb' above). The supported types are string, number, and boolean. ### Positional Parameters Positional parameters can be accessed in a bpftrace program via, what looks like, a numbered scratch variable, e.g. `$1`, `$2`, ..., `$N`. So `$1` would be the first positional parameter and so on. Positional parameters can be placed before or after a double dash, e.g., ``` # bpftrace -e 'begin { print(($1, $2)); }' p1 -- 20 ``` Here `$1` would evaluate to a string `p1` and `$2` would evaluate to a number `20`. If a parameter is used that was not provided, it will default to zero for a numeric context, and "" for a string context. Positional parameters may also be used in probe arguments and will be treated as a string parameter, e.g., `tracepoint:block:block_rq_issue /args.bytes > $1/`. 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. Example: ``` # 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 ``` `$#` is a special builtin that returns the number of positional arguments supplied. ## 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 are supported. ``` if (condition) { ifblock } else if (condition) { if2block } else { elseblock } ``` ## Config Block To improve script portability, you can set bpftrace [Config Variables](#config-variables) via the config block, which can only be placed at the top of the script (in the [preamble](#preamble)) before any action blocks. ``` 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. [List of All Config Variables](#config-variables) ## Config Variables Some behavior can only be controlled through config variables, which are listed here. These can be set via the [Config Block](#config-block) 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 time. 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: true C++ symbol demangling in userspace stack traces is enabled by default. This feature can be turned off by setting the value of this variable to `false`. ### lazy_symbolication Default: false For user space symbols, symbolicate lazily/on-demand (`true`) or symbolicate everything ahead of time (`false`). ### license Default: "GPL" The license bpftrace will use to load BPF programs into the linux kernel. ### 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: 10240 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. ### missing_probes Default: `error` Controls handling of probes which cannot be attached because they do not exist (in the kernel or in the traced binary) or there was an issue during attachment. 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. ### show_debug_info This is only available if the [Blazesym](https://github.com/libbpf/blazesym) library is available at build time. If it is available this defaults to `true`, meaning that when printing ustack and kstack symbols bpftrace will also show (if debug info is available) symbol file and line ('bpftrace' stack mode) and a label if the function was inlined ('bpftrace' and 'perf' stack modes). There might be a performance difference when symbolicating, which is the only reason to disable this. ### 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: true Controls whether maps are printed on exit. Set to `false` in order to change the default behavior and not automatically print maps at program exit. ### unstable features These are the list of unstable features: - `unstable_macro` - feature flag for bpftrace macros - `unstable_map_decl` - feature flag for map declarations - `unstable_tseries` - feature flag for time series map type - `unstable_addr` - feature flag for address of operator (&) All of these accept the following options: Default: warn - `error` - fail if this feature is used - `warn` - enable feature but print a warning - `enable` - enable feature ## 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. | | | | --- | --- | | **Type** | **Description** | | bool | `true` or `false` | | 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]*` ## Keywords `break`, `config`, `continue`, `else`, `for`, `if`, `import`, `let`, `macro`, `offsetof`, `return`, `sizeof`, `unroll`, `while`. * `return` - The return keyword is used to exit the current probe. This differs from `exit()` in that it doesn’t exit bpftrace. ## 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`. These duration suffixes are also supported: `ns`, `us`, `ms`, `s`, `m`, `h`, and `d`. All get turned into integer values in nanoseconds, e.g. ``` $a = 1m; print($a); // prints 60000000000 ``` 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: | | | | --- | --- | | \n | Newline | | \t | Tab | | \0nn | Octal value nn | | \xnn | Hexadecimal value nn | ## Loops ### For `for` loops can be used to iterate over elements in a map, or over a range of integers, provided as two unary expressions separated by `..`. ``` for ($kv : @map) { block; } ``` ``` for ($i : start..end) { block; } ``` The variable declared in the `for` loop will be initialised on each iteration. If the iteration is over a map, the value will be 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 } ``` If 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 } ``` If an integer range is provided, the value will be an integer value for each element in the range, inclusive of the start value and exclusive of the end value: ``` for ($cpu : 0..ncpus) { print($cpu); // current value in range } ``` Note that you cannot adjust the range itself after the loop has started. The `for` start and end values are evaluated once, not on each loop iteration. For example, the following will print `0` through `9`: ``` $a = 10; for ($i : 0..$a) { print($i); $a--; } ``` Both `for` loops support the following control flow statements: | | | | --- | --- | | continue | skip processing of the rest of the block and proceed to the next iteration | | break | terminate the loop | ### While BPF supports `while` loops as long as the verifier can prove they’re bounded and fit within the instruction limit. ``` while (condition) { block; } ``` ``` interval:s:1 { $i = 0; while ($i <= 100) { printf("%d ", $i); if ($i > 5) { break; } $i++ } printf("\n"); } ``` The `while` loop supports the following control flow statements: | | | | --- | --- | | continue | skip processing of the rest of the block and return to the conditional | | break | terminate the loop | ### 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") } ``` ## Macros ***Warning*** this feature is experimental and may be subject to changes. Stabilization is tracked in [#4079](https://github.com/bpftrace/bpftrace/issues/4079). bpftrace macros (as opposed to C macros) provide a way for you to structure your script. They can be useful when you want to factor out code into smaller, more understandable parts. Or if you want to share code between probes. At a high level, macros can be thought of as semantic aware text replacement. They accept (optional) variable, map, and expression arguments. The body of the macro may only access maps and external variables passed in through the arguments, which is why these are often referred to as "hygienic macros", but the macro body can create new variables which only exist inside the body. A macro's parameter signature specifies how an argument will be used. For example `macro test($a, b, @c)` indicates that `$a` needs to be a scratch variable (which might be mutated), that `b` needs to be an expression that will be inserted where ever `b` is used in the macro body, and that `@c` needs to be a map (which might be mutated). A valid use of this macro could be `test($x, 1 + 2, @y)`. Variables and maps can also be used for ident parameters that expect expressions and would be the same as writing `{ @y }` (Block Expression). Here are some valid usages of macros: ``` macro one() { 1 } macro add_one(x) { x + 1 } macro add_one_to_each($a, @b) { $a += 1; @b += 1; } macro side_effects(x) { x; x; x; } macro add_two(x) { add_one(x) + 1 } begin { print(one()); // prints 1 print(one); // prints 1 (bare identifier works if the macro accepts 0 args) $a = 10; print(add_one($a)); // prints 11 print(add_one(1 + 1)); // prints 3 @b = 5; add_one_to_each($a, @b); print($a + @b) // prints 17 side_effects({ printf("hi") }) // prints hihihi print(add_two(1)); // prints 3 } ``` Some examples of invalid macro usage: ``` macro unhygienic_access() { @x++ // BAD: @x not passed in } macro wrong_parameter_type($x) { $x++ } begin { @x = 1; unhygienic_access(); wrong_parameter_type(@x); // BAD: macro expects a scratch variable wrong_parameter_type(1 + 1); // BAD: macro expects a scratch variable } ``` Note: If you want the passed in expression to only be executed once, simply bind it to a variable, e.g., ``` macro add_one(x) { let $x = x; $x + 1 } ``` ## Operators and Expressions ### Arithmetic Operators The following operators are available for integer arithmetic: | | | | --- | --- | | + | 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 | | | | --- | --- | | && | Logical AND | | \|\| | Logical OR | | ! | Logical NOT | ### Bitwise Operators | | | | --- | --- | | & | 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. | | | | --- | --- | | < | 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. | | | | --- | --- | | == | 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: | | | | --- | --- | | = | 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 (`++`) 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++` returns the original value of `x`, before it got incremented while `++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 `++`/`--` on a shared global variable can lose updates. See [`count()`](stdlib.md#count) for more details. ### Block Expressions A block can be used as expression, as long as the last statement of the block is an expression with no trailing semi-colon. ``` let $a = { let $b = 1; $b }; ``` This can be used anywhere an expression can be used. ## Preamble The preamble consists of multiple optional pieces: - preprocessor definitions - type definitions - a [config block](#config-block) - [map declarations](#map-declarations) For example: ``` #include #define RED "\033[31m" struct S { int x; } config = { stack_mode=perf } let @a = lruhash(100); ``` ## 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 [Listing Probes](../man/adoc/bpftrace.adoc#listing-probes). 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); } ``` By default, bpftrace requires all probes to attach successfully or else an error is returned. However this can be changed using the `missing_probes` config variable. 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. | | | | | --- | --- | --- | | **Probe Name** | **Short Name** | **Description** | | [`begin/end`](#beginend) | - | Built-in events | | [`bench`](#bench) | - | Micro benchmarks | | [`self`](#self) | - | Built-in events | | [`hardware`](#hardware) | `h` | Processor-level events | | [`interval`](#interval) | `i` | Timed output | | [`iter`](#iterator) | `it` | Iterators tracing | | [`fentry/fexit`](#fentry-and-fexit) | `f`/`fr` | Kernel functions tracing with BTF support | | [`kprobe/kretprobe`](#kprobe-and-kretprobe) | `k`/`kr` | Kernel function start/return | | [`profile`](#profile) | `p` | Timed sampling | | [`rawtracepoint`](#rawtracepoint) | `rt` | Kernel static tracepoints with raw arguments | | [`software`](#software) | `s` | Kernel software events | | [`tracepoint`](#tracepoint) | `t` | Kernel static tracepoints | | [`uprobe/uretprobe`](#uprobe-uretprobe) | `u`/`ur` | User-level function start/return | | [`usdt`](#usdt) | `U` | User-level static tracepoints | | [`watchpoint/asyncwatchpoint`](#watchpoint-and-asyncwatchpoint) | `w`/`aw` | Memory watchpoints | ### 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); } ``` ### bench `bench` is a special built-in probe type for creating micro benchmarks. bpftrace executes each `bench` probe repeatedly to measure the average execution time of the contained code. If multiple `bench` probes exist in a script, bpftrace executes them sequentially in the order they are specified. To run `bench` probes, you must run bpftrace in bench mode: `bpftrace --test-mode bench ...`; otherwise, `bench` probes will be ignored. ``` bench:lhist { @a = lhist(rand % 10, 1, 10, 1); } bench:count { @b = count(); } bench:my_loop { $a = 0; for ($i : 0..10) { $a++; } } ``` ``` Attached 3 probes +-----------+-------------+ | BENCHMARK | NANOSECONDS | +-----------+-------------+ | count | 40 | | lhist | 88 | | my_loop | 124 | +-----------+-------------+ ``` ### 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. ``` self:signal:SIGUSR1 { print("abc"); } ``` ### 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(); } ``` ### interval **variants** * `interval:count` * `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 [profile](#profile) probes. If a unit of time is not specified in the second position, the number is interpreted as nanoseconds; e.g., `interval:1s`, `interval:1000000000`, and `interval:s:1` are all equivalent. This prints the rate of syscalls per second. ``` tracepoint:raw_syscalls:sys_enter { @syscalls = count(); } interval:1s { print(@syscalls); clear(@syscalls); } ``` ### 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 */ ``` ### fentry and fexit **variants** * `fentry[:module]:fn` * `fexit[:module]:fn` * `fentry:bpf[:prog_id]:prog_name` * `fexit:bpf[:prog_id]:prog_name` **short names** * `f` (`fentry`) * `fr` (`fexit`) **requires (`--info`)** * Kernel features:BTF * Probe types:fentry ``fentry``/``fexit`` probes attach to kernel functions similar to [kprobe and kretprobe](#kprobe-and-kretprobe). 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 [BPF Kernel Functions](https://docs.kernel.org/bpf/kfuncs.html). 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 [Listing Probes](../man/adoc/bpftrace.adoc#listing-probes)). These arguments are also available in the return probe (`fexit`), unlike `kretprobe`. The bpf variants (e.g. `fentry:bpf[:prog_id]:prog_name`) allow attaching to running BPF programs and sub-programs. For example, if bpftrace was already running with a script like `uprobe:./testprogs/uprobe_test:uprobeFunction1 { print("hello"); }` then you could attach to this program with `fexit:bpf:uprobe___testprogs_uprobe_test_uprobeFunction1_1 { print("bye"); }` and this probe would execute after (because it's `fexit`) the `print("hello")` probe executes. You can specify just the program name, and in this case bpftrace will attach to all running programs and sub-programs with that name. You can differentiate between them using the `probe` builtin. You can also specify the program id (e.g. `fentry:bpf:123:*`) to attach to a specific running BPF program or sub-programs called in that running BPF program. To see a list of running, valid BPF programs and sub-programs use `bpftrace -l 'fentry:bpf:*'`. Note: only BPF programs with a BTF Id can be attached to. Also, the `args` builtin is not yet available for this variant. ``` # 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 */ ``` ### 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 [Type conversion](#type-conversion) 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 [BTF Support](#btf-support) 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])); } ``` ### profile **variants** * `profile:count` * `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). If a unit of time is not specified in the second position, the number is interpreted as nanoseconds; e.g., `interval:1s`, `interval:1000000000`, and `interval:s:1` are all equivalent. ``` profile:hz:99 { @[tid] = count(); } ``` ### rawtracepoint **variants** * `rawtracepoint[:module]:event` **short name** * `rt` Raw tracepoints are attached to the same tracepoints as normal tracepoint programs. The reason why you might want to use raw tracepoints over normal tracepoints is due to the performance improvement - [Read More](https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_RAW_TRACEPOINT/). `rawtracepoint` arguments can be accessed via the `argN` builtins AND via the `args` builtin. ``` rawtracepoint:vmlinux:kfree_skb { printf("%llx %llx\n", arg0, args.skb); } ``` `arg0` and `args.skb` will print the same address. `rawtracepoint` 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 arguments accessible by a `rawtracepoint` are different from the arguments you can access from the `tracepoint` of the same name. The function arguments are available in the `args` struct which can be inspected by doing verbose listing (see [Listing Probes](../man/adoc/bpftrace.adoc#listing-probes)). ### 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. ### 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 [Listing Probes](../man/adoc/bpftrace.adoc#listing-probes) 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 ### 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 [kprobe and kretprobe](#kprobe-and-kretprobe) also apply to `uprobe` s and `uretprobe` s, namely: arguments are available via the `argN` builtins and can only be accessed with a uprobe. retval is the return value for the instrumented function and can only be accessed with a uretprobe. **Note**: When tracing some languages, like C++, `arg0` and even `arg1` may refer to runtime internals such as the current object instance (`this`) and/or the eventual return value for large returned objects where copy elision is used. This will push the actual function arguments to possibly start at `arg1` or `arg2` - the only way to know is to experiment. ``` 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 [Listing Probes](../man/adoc/bpftrace.adoc#listing-probes) section for more details. ``` # bpftrace -lv 'uprobe:/bin/bash:rl_set_prompt' uprobe:/bin/bash:rl_set_prompt const char* prompt ``` When tracing C++ 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 ``` ### 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). ### 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 ``` Note that threads are monitored, but only for threads created after watchpoint attachment. The is a limitation from the kernel. Additionally, because of how watchpoints are implemented in bpftrace the specified function must be called at least once in the main thread in order to observe future calls to this function in child threads. ## Pointers Pointers in bpftrace are similar to those found in `C`. ## 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 [Data Types](#data-types). ### 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; } ``` '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; } ``` 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. ### Map Declarations ***Warning*** this feature is experimental and may be subject to changes. Stabilization is tracked in [#4077](https://github.com/bpftrace/bpftrace/issues/4077). Maps can also be declared in the global scope, before probes and after the config e.g. ``` let @a = hash(100); let @b = percpulruhash(20); begin { ... } ``` The utility of this is that you can specify different underlying BPF map types. Currently these are available in bpftrace: - hash (BPF_MAP_TYPE_HASH) - lruhash (BPF_MAP_TYPE_LRU_HASH) - percpuhash (BPF_MAP_TYPE_PERCPU_HASH) - percpulruhash (BPF_MAP_TYPE_LRU_PERCPU_HASH) - percpuarray (BPF_MAP_TYPE_PERCPU_ARRAY) Additionally, map declarations must supply a single argument: ***max entries*** e.g. `let @a = lruhash(100);` All maps that are not declared in the global scope utilize the default set in the config variable "max_map_keys". However, it’s best practice to declare maps up front as using the default can lead to lost map update events (if the map is full) or over allocation of memory if the map is intended to only store a few entries. ***Warning*** The "lru" variants of hash and percpuhash evict the approximately least recently used elements. In other words, users should not rely on the accuracy on the part of the eviction algorithm. Adding a single new element may cause one or multiple elements to be deleted if the map is at capacity. [Read more about LRU internals](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_LRU_HASH/). ### 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)` 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 ...", ...); } } ``` --- ## 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 [kptrs](stdlib.md#kptr) and [uptr](stdlib.md#uptr) functions. ### BPF License By default bpftrace uses "GPL", which is actually "GPL version 2", as the license it uses to load BPF programs into the kernel. Some other examples of compatible licenses are: "GPL v2" and "Dual MPL/GPL". You can specify a different license using the "license" config variable. [Read more about BPF programs and licensing](https://docs.kernel.org/bpf/bpf_licensing.html#using-bpf-programs-in-the-linux-kernel). ### 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 1. 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: 1. 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. 2. Split your program over multiple probes. 3. Check the status of the BPF stack limit in Linux (it may be increased in the future, maybe as a tuneable). 4. (advanced): Run -d and examine the LLVM IR, and look for ways to optimize src/ast/codegen_llvm.cpp. 2. 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. ### 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); } ``` ### PERCPU types For bpftrace PERCPU map types (e.g., those created by using [`count()`](stdlib.md#count) or [`sum()`](stdlib.md#sum)) 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" } } ``` ### Supported architectures x86_64, arm64, s390x, arm32, loongarch64, mips64, ppc64, riscv64 ### Systemd support If bpftrace has been built with `-DENABLE_SYSTEMD=1`, one can 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. ### Unstable Features Some features added to bpftrace are not yet stable. They are enabled by default but come with a warning if used. If you explicitly add the config variable to your script the warning will not be shown e.g. ``` config = { unstable_map_decl=enable; } ``` To opt-out of these unstable features (and ensure they are not used) add the config variable and set it to `error` e.g. ``` config = { unstable_map_decl=error; } ``` Note: all unstable features are subject to change and/or removal. bpftrace-0.24.1/docs/migration_guide.md000066400000000000000000000120551506776124200200210ustar00rootroot00000000000000# 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.23.x (or earlier) to 0.24.x (or later) ### Probe Attachment Failure Exits the Program Previously, if a probe with multiple attach points or a wildcard failed to attach, a warning would be printed and the program would continue to run. Now, if there are any attachment failures, the program will exit with an error. If it's expected that some probes will fail to attach, you can use the config variable 'missing_probes' to either `warn` or `ignore` these failures e.g. ``` config = { missing_probes = "warn" } ``` ## Versions 0.22.x (or earlier) to 0.24.x (or later) After https://github.com/bpftrace/bpftrace/pull/3428 `pid` and `tid` are no longer from the initial namespace. https://github.com/bpftrace/bpftrace/pull/3428 introduced `pid(init)` and `tid(init)` which have the old behavior. Unfortunately, there are no equivalent workarounds in 0.23.x. ## 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; }' Attached 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.24.1/docs/nix.md000066400000000000000000000076731506776124200154630ustar00rootroot00000000000000# 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!") }' Attached 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() }' Attached 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 [...] ``` ### Setup an environment for fuzzing ``` $ nix develop .#bpftrace-fuzz dxu@kashmir bpftrace]$ CC=afl-clang-fast CXX=afl-clang-fast++ cmake -B build-fuzz -DCMAKE_BUILD_TYPE=Debug -DBUILD_ASAN=1 [...] ``` ## Internal examples This section has a few examples on how to interact with the Nix configuration. ### Update flake inputs Flakes have external inputs in the `inputs = { ... }` section of `flake.nix`. To update a single input: ``` $ nix flake lock --update-input blazesym warning: updating lock file '/home/dxu/dev/bpftrace/flake.lock': • Updated input 'blazesym': 'github:libbpf/blazesym/6beb39ebc8e3a604c7b483951c85c831c1bbe0d1' (2025-02-14) → 'github:libbpf/blazesym/285b17f15a12885544b21f1ae352928910656767' (2025-03-04) ``` To update a single input to a specific revision: ``` $ nix flake lock --override-input blazesym github:libbpf/blazesym/6beb39ebc8e3a604c7b483951c85c831c1bbe0d1 warning: updating lock file '/home/dxu/dev/bpftrace/flake.lock': • Updated input 'blazesym': 'github:libbpf/blazesym/285b17f15a12885544b21f1ae352928910656767' (2025-03-04) → 'github:libbpf/blazesym/6beb39ebc8e3a604c7b483951c85c831c1bbe0d1' (2025-02-14) ``` To update all inputs: ``` $ nix flake update warning: updating lock file '/home/dxu/dev/bpftrace/flake.lock': • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/d9b69c3ec2a2e2e971c534065bdd53374bd68b97' (2025-02-24) → 'github:NixOS/nixpkgs/02032da4af073d0f6110540c8677f16d4be0117f' (2025-03-03) ``` ### Format `*.nix` files ``` $ nix fmt 0 / 1 have been reformatted ``` ### Check `*.nix` files for errors ``` $ nix flake check ``` bpftrace-0.24.1/docs/reference_guide.md000066400000000000000000000002151506776124200177610ustar00rootroot00000000000000# bpftrace Reference Guide - [The bpftrace Language](language.md) - [Standard Library](stdlib.md) - [CLI Manual](../man/adoc/bpftrace.adoc) bpftrace-0.24.1/docs/release_process.md000066400000000000000000000117731506776124200200370ustar00rootroot00000000000000# Upcoming release schedule The schedule for the upcoming v0.24 release is: - August 20, 2025: Create release branch `release/0.24.x`. - **September 17, 2025: Release v0.24.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) | ## Preparing for a release Once the release dates are clarified (approximately 6 weeks before the release), do the following steps to track the release in a public manner: 1. Update the release dates at the top of this document and on the [bpftrace website](https://github.com/bpftrace/website/blob/master/src/pages/release-schedule.md). 1. Create a new tracker issue in GitHub from the "Release tracker" template. ## 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.24.1/docs/stdlib.md000066400000000000000000001261701506776124200161400ustar00rootroot00000000000000# bpftrace Standard Library This includes builtins, functions, macros, and [map value functions](#map-value-functions). The boundaries for the first three are blurred, by design, to allow for more flexible usage and are grouped below as "Helpers". For example `pid` and `pid()` are equivalent; both yielding the process id. Basically all functions or macros that don't have arguments or have default arguments can be invoked with or without the call syntax. **async** helpers are asynchronous, which can lead to unexpected behaviour. See the [Invocation Mode](#invocation-mode) section for more information. **compile time** helpers are evaluated at compile time, a static value will be compiled into the program. **unsafe** helpers can have dangerous side effects and should be used with care, the `--unsafe` flag is required for use. ## Helpers ### assert - `void assert(bool condition, string message)` Simple assertion macro that will exit the entire script with an error code if the condition is not met. ### bswap - `uint8 bswap(uint8 n)` - `uint16 bswap(uint16 n)` - `uint32 bswap(uint32 n)` - `uint64 bswap(uint64 n)` `bswap` reverses the order of the bytes in integer `n`. In case of 8 bit integers, `n` is returned without being modified. The return type is an unsigned integer of the same width as `n`. ### buf - `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 ``` ### cat - `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 ``` ### cgroup - `uint64 cgroup()` - `uint64 cgroup` ID of the cgroup the current process belongs to Only works with cgroupv2 This utilizes the BPF helper `get_current_cgroup_id` ### cgroup_path - `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 */ } ``` ### cgroupid - `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")); } ``` ### clear - `void clear(map m)` **async** Clear all keys/values from map `m`. ``` interval:ms:100 { @[rand % 10] = count(); } interval:s:10 { print(@); clear(@); } ``` ### comm - `string comm()` - `string comm` Name of the current thread This utilizes the BPF helper `get_current_comm` ### cpid - `uint32 cpid()` - `uint32 cpid` Child process ID, if bpftrace is invoked with `-c` ### cpu - `uint32 cpu()` - `uint32 cpu` ID of the processor executing the BPF program BPF program, in this case, is the probe body This utilizes the BPF helper `raw_smp_processor_id` ### curtask - `uint64 curtask()` - `uint64 curtask` Pointer to `struct task_struct` of the current task This utilizes the BPF helper `get_current_task` ### delete - `bool delete(map m, mapkey k)` - deprecated `bool 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"));` If deletion fails (e.g. the key doesn’t exist) the function returns false (0). Additionally, if the return value for `delete` is discarded, and deletion fails, you will get a warning. ``` @a[1] = 1; delete(@a, 1); // no warning (the key exists) if (delete(@a, 2)) { // no warning (return value is used) ... } $did_delete = delete(@a, 2); // no warning (return value is used) delete(@a, 2); // warning (return value is discarded and the key doesn’t exist) ``` 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]); } ``` ### elapsed - `uint64 elapsed()` - `uint64 elapsed` ktime_get_ns - ktime_get_boot_ns ### errorf - `void errorf(const string fmt, args...)` **async** `errorf()` formats and prints data (similar to [`printf`](#printf)) as an error message with the source location. ``` BEGIN { errorf("Something bad with args: %d, %s", 10, "arg2"); } ``` Prints: ``` EXPECT stdin:1:9-62: ERROR: Something bad with args: 10, arg2 ``` ### exit - `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); } ``` ### func - `string func()` - `string func` Name of the current function being traced (kprobes,uprobes,fentry) ### getopt - `bool getopt(string arg_name)` - `string getopt(string arg_name, string default_value)` - `int getopt(string arg_name, int default_value)` - `bool getopt(string arg_name, bool default_value)` Get the named command line argument/option e.g. ``` # bpftrace -e 'BEGIN { print(getopt("hello", 1)); }' -- --hello=5 ``` `getopt` defines the type of the argument by the default value’s type. If no default type is provided, the option is treated like a boolean arg e.g. `getopt("hello")` would evaluate to `false` if `--hello` is not specified on the command line or `true` if `--hello` is passed or set to one of the following values: `true`, `1`. Additionally, boolean args accept the following false values: `0`, `false` e.g. `--hello=false`. If the arg is not set on the command line, the default value is used. ``` # bpftrace -e 'BEGIN { print((getopt("aa", 10), getopt("bb", "hello"), getopt("cc"), getopt("dd", false))); }' -- --cc --bb=bye ``` ### gid - `uint64 gid()` - `uint64 gid` Group ID of the current thread, as seen from the init namespace This utilizes the BPF helper `get_current_uid_gid` ### has_key - `boolean 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 } ``` ### jiffies - `uint64 jiffies()` - `uint64 jiffies` Jiffies of the kernel On 32-bit systems, using this builtin might be slower This utilizes the BPF helper `get_jiffies_64` ### join - `void join(char *arr[], [char * sep = ' '])` **async** `join` joins a char * `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); } ``` ### kaddr - `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`. ### kptr - `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. ### kstack - `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 */ ``` ### ksym - `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 */ ``` ### len - `int64 len(map m)` - `int64 len(ustack stack)` - `int64 len(kstack stack)` For maps, return the number of elements in the map. For kstack/ustack, return the depth (measured in # of frames) of the call stack. ### macaddr - `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 */ ``` ### ncpus - `uint64 ncpus()` - `uint64 ncpus` Number of CPUs ### nsecs - `timestamp nsecs([TimestampMode mode])` - `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.` Returns a timestamp in nanoseconds, as given by the requested kernel clock. Defaults to `boot` if no clock is explicitly requested. ``` 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 * ... */ ``` ### ntop - `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. ### numaid - `uint32 numaid()` - `uint32 numaid` ID of the NUMA node executing the BPF program BPF program, in this case, is the probe body This utilizes the BPF helper `numa_node_id` ### offsetof - `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(); } ``` ### override - `void override(uint64 rc)` **unsafe** **Kernel** 4.16 This utilizes the BPF 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' ``` ### path - `char * path(struct path * path [, int32 size])` **Kernel** 5.10 This utilizes the BPF 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. ### percpu_kaddr - `uint64 *percpu_kaddr(const string name)` - `uint64 *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); } } ``` ### pid - `uint32 pid([curr_ns|init])` - `uint32 pid` Returns the process ID of the current thread. Defaults to `curr_ns`. * `pid(curr_ns)` - The process ID as seen from the PID namespace of bpftrace. * `pid(init)` - The process ID as seen from the initial PID namespace. ### ppid - `uint32 ppid(struct task_struct * task)` Get the pid of the parent process ### print - `void print(T val)` **async** ### printf - `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: | Specifier | Type | Description | | --- | --- | --- | | r | buffer | Hex-formatted string to print arbitrary binary content returned by the [buf](#buf) 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") ``` ### probe - `string probe()` - `string probe` Name of the fully expanded probe For example: `kprobe:do_nanosleep` ### pton - `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. ### rand - `uint32 rand()` - `uint32 rand` Get a pseudo random number This utilizes the BPF helper `get_prandom_u32` ### reg - `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. ### retval - `uint64 retval()` - `uint64 retval` 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` ### signal - `void signal(const string sig)` - `void signal(uint32 signum)` **unsafe** **Kernel** 5.3 This utilizes the BPF 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) ``` ### sizeof - `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. ### skboutput - `uint32 skboutput(const string path, struct sk_buff *skb, uint64 length, const uint64 offset)` **Kernel** 5.5 This utilizes the BPF 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) ``` ### socket_cookie - `uint64 socket_cookie(struct sock *sk)` This utilizes the BPF helper `bpf_get_socket_cookie` Retrieve the cookie (generated by the kernel) of the socket. If no cookie has been set yet, generate a new cookie. Once generated, the socket cookie remains stable for the life of the socket. This function returns a `uint64` unique number on success, or 0 if **sk** is NULL. ``` fentry:tcp_rcv_established { $cookie = socket_cookie(args->sk); @psize[$cookie] = hist(args->skb->len); } ``` Prints: ``` @psize[65551]: [32, 64) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @psize[504]: [32, 64) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 1 |@@@@@@@@@@@@@ | [128, 256) 0 | | [256, 512) 1 |@@@@@@@@@@@@@ | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 1 |@@@@@@@@@@@@@ | ``` ### str - `string str(char * data [, uint32 length)` This utilizes the BPF helpers `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 [Address-spaces](./language.md#address-spaces) for more information. ### strcontains - `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. ### strerror - `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)); } ``` ### strftime - `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: | Specifier | Description | | --- | --- | | `%f` | Microsecond as a decimal number, zero-padded on the left | ### strncmp - `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. ### system - `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`. ``` Attached 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); } ``` ### tid - `uint32 tid([curr_ns|init])` - `uint32 tid` Returns the thread ID of the current thread. Defaults to `curr_ns`. * `tid(curr_ns)` - The thread ID as seen from the PID namespace of bpftrace. * `tid(init)` - The thread ID as seen from the initial PID namespace. ### time - `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. ### uaddr - `T * uaddr(const string sym)` **Supported probes** * uprobes * uretprobes * USDT ***Does not work with ASLR, see issue [#75](https://github.com/bpftrace/bpftrace/issues/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"))); } ``` ### uid - `uint64 uid()` - `uint64 uid` User ID of the current thread, as seen from the init namespace This utilizes the BPF helper `get_current_uid_gid` ### unwatch - `void unwatch(void * addr)` **async** Removes a watchpoint ### uptr - `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. ### usermode - `uint8 usermode()` - `uint8 usermode` Returns 1 if the current process is in user mode, 0 otherwise Currently only available on x86_64. ### username - `string username()` - `string username` Get the current username Often this is just "root" ### ustack - `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. ### usym - `usym_t usym(uint64 * addr)` **async** **Supported probes** * uprobes * uretprobes Equal to [ksym](#ksym) 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 */ ``` ### zero - `void zero(map m)` **async** Set all values (for all keys) in the map to zero. ## Map Value Functions Map value functions can only be assigned to maps (when scalar) or map keys. The data types associated with these functions are only for internal use but many can be cast to integers (e.g. `count_t` and `sum_t`). ### avg * `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. ### count * `count_t count()` Count how often this function is called. Using `@=count()` is conceptually similar to `@++`. The difference is that the `count()` function uses a map type optimized for performance and correctness using cheap, thread-safe writes ([PERCPU](./language.md#percpu-types)). 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. `@++`) where multiple writers to a shared location might lose updates, as bpftrace does not generate any atomic instructions for `++`. 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(@); } ``` ### hist * `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 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` ### lhist * `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 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` ### max * `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 [PERCPU](./language.md#percpu-types) 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. ### min * `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 [PERCPU](./language.md#percpu-types) map (thread-safe, fast writes, slow reads). See `max()` above for how this differs from the typical userspace `min()`. ### stats * `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 @ ``` ### sum * `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 ([PERCPU](./language.md#percpu-types)). 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(@); } ``` ### tseries * `tseries_t tseries(int64 n, int64 interval_ns, int64 num_intervals)` * `tseries_t tseries(int64 n, int64 interval_ns, int64 num_intervals, const string agg)` Create a time series that tracks an integer value. `tseries` records up to `num_intervals` intervals representing `interval_ns` nanoseconds. #### Durations `interval_ns` is an unsigned integer that specifies the interval duration. You may use numbers with duration suffixes to improve readability: ``` @a = tseries(1, 100ns, 5); // 100 nanoseconds @b = tseries(1, 100us, 5); // 100 microseconds @c = tseries(1, 100ms, 5); // 100 milliseconds @d = tseries(1, 1s, 5); // 1 second ``` #### Aggregation Functions By default, each interval in `tseries` contains the last value recorded in that interval. The optional `agg` parameter specifies how values in the same interval are aggregated. | Aggregation Function | Example | Description | | --- | --- | --- | | `avg` | `@ = tseries(@v, 1s, 5, "avg")` | Calculate the running average of all the values in each interval. | | `max` | `@ = tseries(@v, 1s, 5, "max")` | Calculate the maximum of all values in each interval. | | `min` | `@ = tseries(@v, 1s, 5, "min")` | Calculate the minimum of all values in each interval. | | `sum` | `@ = tseries(@v, 1s, 5, "sum")` | Calculate the sum of all values in each interval. | #### Examples Example one: ``` // Record the minimum of ten random values generated during each 100ms interval. i:ms:10 { @ = tseries(rand % 10, 100ms, 20, "min"); } ``` ``` Attached 2 probes @: 0 2 hh:mm:ss.ms |___________________________________________________| 10:41:46.700 * | 0 10:41:46.800 * | 0 10:41:46.900 | * | 1 10:41:47.000 | * 2 10:41:47.100 | * | 1 10:41:47.200 * | 0 10:41:47.300 | * | 1 10:41:47.400 * | 0 10:41:47.500 | * | 1 10:41:47.600 * | 0 10:41:47.700 | * 2 10:41:47.800 * | 0 10:41:47.900 * | 0 10:41:48.000 | * | 1 10:41:48.100 | * | 1 10:41:48.200 | * | 1 10:41:48.300 * | 0 10:41:48.400 | * 2 10:41:48.500 | * | 1 10:41:48.600 | * | 1 v___________________________________________________v 0 2 ``` Example two: ``` // Create a zigzag pattern BEGIN { @dir = 1; @c = -5; } i:ms:100 { @ = tseries(@c, 100ms, 20); @c += @dir; if (@c > 5) { @dir = -1; @c = 4 } else if (@c < -5) { @dir = 1; @c = -4; } } ``` ``` Attached 2 probes @: -5 5 hh:mm:ss.ms |___________________________________________________| 10:39:49.300 * . | -5 10:39:49.400 | * . | -4 10:39:49.500 | * . | -3 10:39:49.600 | * . | -2 10:39:49.700 | * . | -1 10:39:49.800 | * | 0 10:39:49.900 | . * | 1 10:39:50.000 | . * | 2 10:39:50.100 | . * | 3 10:39:50.200 | . * | 4 10:39:50.300 | . * 5 10:39:50.400 | . * | 4 10:39:50.500 | . * | 3 10:39:50.600 | . * | 2 10:39:50.700 | . * | 1 10:39:50.800 | * | 0 10:39:50.900 | * . | -1 10:39:51.000 | * . | -2 10:39:51.100 | * . | -3 10:39:51.200 | * . | -4 v___________________________________________________v -5 5 ``` ## Invocation Mode There are three invocation modes for bpftrace built-in functions. | | | | | --- | --- | --- | | 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. bpftrace-0.24.1/docs/tutorial_one_liners.md000066400000000000000000000421111506776124200207270ustar00rootroot00000000000000# 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. Read more about [the language](language.md), [the standard library](stdlib.md), and [CLI options](../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"); }' Attached 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)); }' Attached 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(); }' Attached 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); }' Attached 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); }' Attached 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); }' Attached 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(); }' Attached 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(); }' Attached 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); }' Attached 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 Attached 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.24.1/docs/tutorial_one_liners_chinese.md000066400000000000000000000377411506776124200224420ustar00rootroot00000000000000# 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"); }' Attached 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)); }' Attached 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(); }' Attached 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); }' Attached 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); }' Attached 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); }' Attached 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(); }' Attached 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(); }' Attached 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); }' Attached 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 Attached 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.24.1/docs/tutorial_one_liners_japanese.md000066400000000000000000000526761506776124200226160ustar00rootroot00000000000000# 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"); }' Attached 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)); }' Attached 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(); }' Attached 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); }' Attached 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); }' Attached 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); }' Attached 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(); }' Attached 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(); }' Attached 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); }' Attached 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 Attached 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.24.1/flake.lock000066400000000000000000000110651506776124200153350ustar00rootroot00000000000000{ "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": 1750785918, "narHash": "sha256-3xBEvLWE+UfLKHrnBrhM9iTF+cvD0shBheME2g+GeKY=", "owner": "libbpf", "repo": "blazesym", "rev": "4d2e36289c647480bcea84e158aa852624bfac69", "type": "github" }, "original": { "owner": "libbpf", "repo": "blazesym", "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": 1757034884, "narHash": "sha256-PgLSZDBEWUHpfTRfFyklmiiLBE1i1aGCtz4eRA3POao=", "owner": "NixOS", "repo": "nixpkgs", "rev": "ca77296380960cd497a765102eeb1356eb80fed0", "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.24.1/flake.nix000066400000000000000000000327071506776124200152110ustar00rootroot00000000000000{ 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"; 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 = 21; # Override to specify the libbpf build we want. Note that we need to # capture a specific fix for linking which is not yet present in a # release. Once this fix is present in a release, then this should be # updated to the relevant version and we need to update the version # constraint in `CMakeLists.txt`. libbpfVersion = "5e3306e89a44cab09693ce4bfe50bfc0cb595941"; libbpf = pkgs.libbpf.overrideAttrs { version = libbpfVersion; src = pkgs.fetchFromGitHub { owner = "libbpf"; repo = "libbpf"; rev = "${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-giMF2DaBDk3/MKQkCzYcn5ZkcuCyrPXXoe9jI5E3QI0="; }; }; # Override to specify the bcc build we want. # First overrides with the above libbpf and then overrides the rev. # We need a specific patch in BCC which resolves a build failure with # LLVM 21 and is not a part of any official release, yet. bccVersion = "8c5c96ad3beeed2fa827017f451a952306826974"; bcc = (pkgs.bcc.override { libbpf = libbpf; llvmPackages = pkgs."llvmPackages_${toString defaultLlvmVersion}"; }).overridePythonAttrs { version = bccVersion; src = pkgs.fetchFromGitHub { owner = "iovisor"; repo = "bcc"; rev = "${bccVersion}"; # See above sha256 = "sha256-XcTqcsbyUBe83vsjUC70GoffCXaxk32QddBKFEP6LD8="; }; }; # 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.bpftools pkgs."llvmPackages_${toString llvmVersion}".clang pkgs.cmake pkgs.flex pkgs.gcc pkgs.ninja pkgs.pkg-config ]; buildInputs = [ bcc blazesym_c libbpf pkgs.asciidoctor pkgs.cereal pkgs.elfutils pkgs.gtest pkgs.libbfd pkgs.libelf pkgs.libffi pkgs.libopcodes pkgs.libpcap pkgs.systemdLibs 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" "-DENABLE_SYSTEMD=1" ]; # 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 LLVM version as input mkBpftraceDevShell = llvmVersion: let pkg = self.packages.${system}."bpftrace-llvm${toString llvmVersion}"; in with pkgs; pkgs.mkShell { buildInputs = [ bc binutils bpftools coreutils pkgs."llvmPackages_${toString llvmVersion}".clang-tools # Needed for the nix-aware "wrapped" clang-tidy gawk git gnugrep go # For runtime tests iproute2 kmod # For git-clang-format pkgs."llvmPackages_${toString llvmVersion}".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" ]; }; # Ensure that the LLVM & clang version for AFL are aligned, and can # be controlled alongside the version used for the shell environment. mkAFL = llvmVersion: pkgs.aflplusplus.override { clang = pkgs."clang_${toString llvmVersion}"; llvm = pkgs."llvmPackages_${toString llvmVersion}".llvm; llvmPackages = pkgs."llvmPackages_${toString llvmVersion}"; }; # Lambda that can be used for a fuzzing environment. Not part of the default # devshell because aflplusplus is only available for x86_64/amd64. mkBpftraceFuzzShell = llvmVersion: shell: let afl = mkAFL llvmVersion; in with pkgs; pkgs.mkShell { nativeBuildInputs = shell.nativeBuildInputs; buildInputs = [ afl ] ++ shell.buildInputs; # See above. 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-llvm21 = mkBpftrace 21; 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 kernel-6_14 = mkKernel "6.14.4" "sha256:0gvbw38vmbccvz64b3ljqiwkkgil0hgnlakpdjang038pxsxddmr"; }; # 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-llvm21 = mkBpftraceDevShell 21; bpftrace-llvm20 = mkBpftraceDevShell 20; bpftrace-llvm19 = mkBpftraceDevShell 19; bpftrace-llvm18 = mkBpftraceDevShell 18; bpftrace-llvm17 = mkBpftraceDevShell 17; bpftrace-llvm16 = mkBpftraceDevShell 16; # Note that we depend on LLVM 18 explicitly for the fuzz shell, and # this is managed separately. The version of LLVM used to build the # tool must be the same as the version linked as a dependency, or # strange things happen. Hopefully this is a simple update, where # both numbers are bumped at the same time. bpftrace-fuzz = mkBpftraceFuzzShell 18 self.devShells.${system}."bpftrace-llvm18"; }; }); } bpftrace-0.24.1/images/000077500000000000000000000000001506776124200146435ustar00rootroot00000000000000bpftrace-0.24.1/images/bpftrace.graffle000066400000000000000000005120101506776124200177600ustar00rootroot00000000000000 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.24.1/images/bpftrace_Full_Logo-Black.svg000066400000000000000000000177171506776124200221430ustar00rootroot00000000000000 bpftrace-0.24.1/images/bpftrace_Full_Logo-White.svg000066400000000000000000000177171506776124200222070ustar00rootroot00000000000000 bpftrace-0.24.1/images/bpftrace_probes_2018.png000066400000000000000000007522231506776124200211760ustar00rootroot00000000000000‰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.24.1/images/ci_appimage_artifact.png000066400000000000000000001701541506776124200214740ustar00rootroot00000000000000‰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.24.1/images/ci_job_env.png000066400000000000000000002452351506776124200174610ustar00rootroot00000000000000‰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.24.1/man/000077500000000000000000000000001506776124200141515ustar00rootroot00000000000000bpftrace-0.24.1/man/CMakeLists.txt000066400000000000000000000001431506776124200167070ustar00rootroot00000000000000add_custom_target(man ALL DEPENDS adoc_man man_man) add_subdirectory(adoc) add_subdirectory(man8) bpftrace-0.24.1/man/adoc/000077500000000000000000000000001506776124200150575ustar00rootroot00000000000000bpftrace-0.24.1/man/adoc/CMakeLists.txt000066400000000000000000000016421506776124200176220ustar00rootroot00000000000000find_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.24.1/man/adoc/bpftrace.adoc000066400000000000000000000375621506776124200175120ustar00rootroot00000000000000= 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 use of detected features, valid values are:: *uprobe_multi* to disable uprobe_multi link + *kprobe_multi* to disable kprobe_multi link + *kprobe_session* to disable automatic collapse of kprobe/kretprobe into kprobe session === *--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 or filter actions by _PID_. If the process terminates, bpftrace will also terminate. When using USDT, uprobes, uretprobes, hardware, software, profile, interval, watchpoint, or asyncwatchpoint probes they will be attached to only this process. For all other probes, except begin/end, the pid will act like a predicate to filter out events not from that pid. 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. === Program Options You can also pass custom options to a bpftrace program/script itself via positional or named parameters. Positional parameters can be placed before or after a double dash but named parameters can ONLY come after a double dash; e.g. ``` # bpftrace -e 'begin { print(($1, $2, getopt("aa", 1), getopt("bb"))); }' p1 -- --aa=20 --bb p2 // (p1, p2, 20, true) is printed ``` or ``` # bpftrace myscript.bt -- p1 --aa=20 --bb p2 ``` In these examples there are two positional parameters (`p1`, `p2`) and two named parameters (`aa`, which is set to `20`, and `bb`, which is set to `true`). Named program parameters require the `=` to set their value unless they are boolean parameters (like 'bb' above). Read about how to access positional and named parameters link:https://github.com/bpftrace/bpftrace/blob/master/docs/language.md#command-line-parameters[here]. == The Language Syntax, types, and concepts for bpftrace are link:https://github.com/bpftrace/bpftrace/blob/master/docs/language.md[available here]. == 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. link:https://github.com/bpftrace/bpftrace/blob/master/docs/language.md#probes[Full list of probe types]. == Standard library The standard library of all link:https://github.com/bpftrace/bpftrace/blob/master/docs/stdlib.md#builtins[builtins], link:https://github.com/bpftrace/bpftrace/blob/master/docs/stdlib.md#functions[functions], and link:https://github.com/bpftrace/bpftrace/blob/master/docs/stdlib.md#map-functions[map functions] is link:https://github.com/bpftrace/bpftrace/blob/master/docs/stdlib.md[available here]. == Configuration === Config Variables Some behavior can only be controlled through config variables, which are link:https://github.com/bpftrace/bpftrace/blob/master/docs/language.md#config-variables[available here]. These can be set via the Config Block 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`. === Environment Variables These are not available as part of the standard set of Config Variables above 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. == 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 Attached 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)); }' Attached 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 Attached 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. [...] ---- == 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. |=== == 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 Attached 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 Attached 1 probe iscsid is sleeping. iscsid is sleeping. ---- bpftrace-0.24.1/man/man8/000077500000000000000000000000001506776124200150145ustar00rootroot00000000000000bpftrace-0.24.1/man/man8/CMakeLists.txt000066400000000000000000000007441506776124200175610ustar00rootroot00000000000000find_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.24.1/man/man8/bashreadline.bt.8000066400000000000000000000025671506776124200201440ustar00rootroot00000000000000.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.24.1/man/man8/biolatency.bt.8000066400000000000000000000037171506776124200176520ustar00rootroot00000000000000.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.24.1/man/man8/biosnoop.bt.8000066400000000000000000000034331506776124200173440ustar00rootroot00000000000000.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.24.1/man/man8/biostacks.bt.8000066400000000000000000000032401506776124200174720ustar00rootroot00000000000000.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.24.1/man/man8/bitesize.bt.8000066400000000000000000000030061506776124200173260ustar00rootroot00000000000000.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.24.1/man/man8/capable.bt.8000066400000000000000000000027041506776124200171030ustar00rootroot00000000000000.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.24.1/man/man8/cpuwalk.bt.8000066400000000000000000000023141506776124200171570ustar00rootroot00000000000000.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.24.1/man/man8/dcsnoop.bt.8000066400000000000000000000040211506776124200171530ustar00rootroot00000000000000.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.24.1/man/man8/execsnoop.bt.8000066400000000000000000000035301506776124200175150ustar00rootroot00000000000000.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.24.1/man/man8/gethostlatency.bt.8000066400000000000000000000033401506776124200205460ustar00rootroot00000000000000.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.24.1/man/man8/killsnoop.bt.8000066400000000000000000000032661506776124200175320ustar00rootroot00000000000000.TH killsnoop.bt 8 "2018-09-07" "USER COMMANDS" .SH NAME killsnoop.bt \- Trace signals issued by the kill(),tkill(),tgkill() syscall. Uses bpftrace/eBPF. .SH SYNOPSIS .B killsnoop.bt .SH DESCRIPTION killsnoop traces the kill(),tkill(),tgkill() 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(),tkill(),tgkill() 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(),tkill(),tgkill() 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()|tkill()|tgkill()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.24.1/man/man8/loads.bt.8000066400000000000000000000037401506776124200166170ustar00rootroot00000000000000.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.24.1/man/man8/mdflush.bt.8000066400000000000000000000032221506776124200171520ustar00rootroot00000000000000.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.24.1/man/man8/naptime.bt.8000066400000000000000000000030021506776124200171410ustar00rootroot00000000000000.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.24.1/man/man8/oomkill.bt.8000066400000000000000000000034201506776124200171560ustar00rootroot00000000000000.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.24.1/man/man8/opensnoop.bt.8000066400000000000000000000031121506776124200175260ustar00rootroot00000000000000.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.24.1/man/man8/pidpersec.bt.8000066400000000000000000000026611506776124200174740ustar00rootroot00000000000000.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.24.1/man/man8/runqlat.bt.8000066400000000000000000000034321506776124200172010ustar00rootroot00000000000000.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.24.1/man/man8/runqlen.bt.8000066400000000000000000000031021506776124200171710ustar00rootroot00000000000000.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.24.1/man/man8/setuids.bt.8000066400000000000000000000030031506776124200171650ustar00rootroot00000000000000.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.24.1/man/man8/ssllatency.bt.8000066400000000000000000000037321506776124200176770ustar00rootroot00000000000000.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.24.1/man/man8/sslsnoop.bt.8000066400000000000000000000031771506776124200174010ustar00rootroot00000000000000.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.24.1/man/man8/statsnoop.bt.8000066400000000000000000000033151506776124200175450ustar00rootroot00000000000000.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.24.1/man/man8/swapin.bt.8000066400000000000000000000030511506776124200170110ustar00rootroot00000000000000.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.24.1/man/man8/syncsnoop.bt.8000066400000000000000000000030571506776124200175510ustar00rootroot00000000000000.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.24.1/man/man8/syscount.bt.8000066400000000000000000000044521506776124200174050ustar00rootroot00000000000000.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.24.1/man/man8/tcpaccept.bt.8000066400000000000000000000041511506776124200174600ustar00rootroot00000000000000.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.24.1/man/man8/tcpconnect.bt.8000066400000000000000000000036041506776124200176540ustar00rootroot00000000000000.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.24.1/man/man8/tcpdrop.bt.8000066400000000000000000000041031506776124200171620ustar00rootroot00000000000000.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.24.1/man/man8/tcplife.bt.8000066400000000000000000000044061506776124200171430ustar00rootroot00000000000000.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.24.1/man/man8/tcpretrans.bt.8000066400000000000000000000037651506776124200177110ustar00rootroot00000000000000.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.24.1/man/man8/tcpsynbl.bt.8000066400000000000000000000035011506776124200173460ustar00rootroot00000000000000.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.24.1/man/man8/threadsnoop.bt.8000066400000000000000000000032031506776124200200350ustar00rootroot00000000000000.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.24.1/man/man8/undump.bt.8000066400000000000000000000023731506776124200170260ustar00rootroot00000000000000.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.24.1/man/man8/vfscount.bt.8000066400000000000000000000032311506776124200173570ustar00rootroot00000000000000.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.24.1/man/man8/vfsstat.bt.8000066400000000000000000000034351506776124200172100ustar00rootroot00000000000000.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.24.1/man/man8/writeback.bt.8000066400000000000000000000033211506776124200174630ustar00rootroot00000000000000.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.24.1/man/man8/xfsdist.bt.8000066400000000000000000000034371506776124200172040ustar00rootroot00000000000000.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.24.1/scripts/000077500000000000000000000000001506776124200150655ustar00rootroot00000000000000bpftrace-0.24.1/scripts/bash-completion/000077500000000000000000000000001506776124200201515ustar00rootroot00000000000000bpftrace-0.24.1/scripts/bash-completion/bpftrace000066400000000000000000000033331506776124200216640ustar00rootroot00000000000000# 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.24.1/scripts/check_kernel_features.sh000077500000000000000000000023401506776124200217360ustar00rootroot00000000000000#!/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.24.1/scripts/clang_tidy.sh000077500000000000000000000035441506776124200175470ustar00rootroot00000000000000#!/usr/bin/env 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.24.1/scripts/compare_tool_codegen.sh000077500000000000000000000033261506776124200215770ustar00rootroot00000000000000#!/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.24.1/scripts/compare_tool_elf.sh000077500000000000000000000031551506776124200207410ustar00rootroot00000000000000#!/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.24.1/scripts/compare_tool_speed.sh000077500000000000000000000041251506776124200212710ustar00rootroot00000000000000#!/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.24.1/scripts/create-assets.sh000077500000000000000000000033131506776124200201670ustar00rootroot00000000000000#!/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 "$TMP/bin" chmod +x "$TMP/bin/"*.bt 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.24.1/scripts/generate_stdlib_docs.py000077500000000000000000000145371506776124200216170ustar00rootroot00000000000000#!/usr/bin/env python3 """ Python script to generate documentation in stdlib.md """ import glob import os import sys import re from typing import Optional class Helper: name: str = "" variants: list[str] = [] deprecated_variants: list[str] = [] description: str = "" def __init__(self): self.variants = [] self.deprecated_variants = [] def parse_variant_string(line: str) -> Optional[str]: pattern = r':variant\s+(.+)' match = re.match(pattern, line.strip()) if match: return match.group(1).strip() return None def parse_deprecated_variant_string(line: str) -> Optional[str]: pattern = r':deprecated_variant\s+(.+)' match = re.match(pattern, line.strip()) if match: return match.group(1).strip() return None def variant_has_no_args(variant: str) -> bool: return "()" in variant def parse_function_name_string(line: str) -> Optional[str]: pattern = r':function\s+(.+)' match = re.match(pattern, line.strip()) if match: return match.group(1).strip() return None def parse_macro_name(line: str) -> Optional[str]: # Pattern to match "macro name(" pattern = r'macro\s+([^(]+)\(' match = re.match(pattern, line.strip()) if match: return match.group(1).strip() return None def read_file_lines(file_path: str) -> Optional[list[Helper]]: helpers: list[Helper] = [] try: if not os.path.exists(file_path): print(f"Error: File '{file_path}' not found.") return None current = Helper() with open(file_path, 'r', encoding='utf-8') as file: for line in file: line_content = line.lstrip().rstrip() if line_content.startswith("//"): # Don't strip, because we may have markdown with meaningful # whitespace at the beginning of the line that we preserve. line_content = line_content[2:] if len(line_content) > 0 and line_content[:1].isspace(): line_content = line_content[1:] if line_content.startswith(":variant"): parsed_variant = parse_variant_string(line_content) if parsed_variant: current.variants.append(parsed_variant) if variant_has_no_args(parsed_variant): current.variants.append(parsed_variant.replace("()", "")) elif line_content.startswith(":deprecated_variant"): parsed_variant = parse_deprecated_variant_string(line_content) if parsed_variant: current.deprecated_variants.append(parsed_variant) elif line_content.startswith(":function"): parsed_function_name = parse_function_name_string(line_content) if parsed_function_name: current.name = parsed_function_name elif line_content or current.description: current.description += line_content + "\n" elif line_content.startswith("macro"): parsed_name = parse_macro_name(line_content) if parsed_name: current.name = parsed_name # There must at least be a description or it's an # undocumented macro. if current.description: helpers.append(current) else: print(f"Warning: Helper '{current.name}' will not be added to the docs.") current = Helper() else: if current.name and current.description: helpers.append(current) current = Helper() if current.name and current.description: helpers.append(current) return helpers except PermissionError: print(f"Error: Permission denied to read '{file_path}'.") return None except UnicodeDecodeError: print(f"Error: Unable to decode '{file_path}' as UTF-8.") return None except Exception as e: print(f"Error reading file '{file_path}': {e}") return None def write_markdown_doc(helpers: list[Helper]): print("Writing markdown file to docs/stdlib.md") current_lines = [] with open("docs/stdlib.md", "r", encoding="utf-8") as file: current_lines = file.readlines() in_helpers_section = False cleaned_lines = [] for raw_line in current_lines: line = raw_line.strip() if line == "## Helpers": in_helpers_section = True elif line == "## Map Value Functions": in_helpers_section = False elif in_helpers_section: continue cleaned_lines.append(raw_line) updated_lines = [] for cleaned_line in cleaned_lines: line = cleaned_line.strip() if line == "## Helpers": updated_lines.append(cleaned_line) updated_lines.append("\n") for helper in helpers: updated_lines.append(f"### {helper.name}\n") for variant in helper.variants: updated_lines.append(f"- `{variant}`\n") for variant in helper.deprecated_variants: updated_lines.append(f"- deprecated `{variant}`\n") updated_lines.append("\n") updated_lines.append(f"{helper.description}\n") updated_lines.append("\n") else: updated_lines.append(cleaned_line) with open("docs/stdlib.md", "w", encoding="utf-8") as file: file.writelines(updated_lines) def main(): stdlib_files = glob.glob("src/stdlib/**/*.bt", recursive=True) if len(stdlib_files) == 0: print("Didn't find any stdlib files") sys.exit(1) else: print(f"All stdlib files: {stdlib_files}") all_helpers: list[Helper] = [] for file in stdlib_files: helpers = read_file_lines(file) if helpers: all_helpers += helpers if len(all_helpers) > 0: sorted_by_name = sorted(all_helpers, key=lambda h: h.name) write_markdown_doc(sorted_by_name) else: sys.exit(1) if __name__ == "__main__": main() bpftrace-0.24.1/scripts/seccomp.c000066400000000000000000000130001506776124200166540ustar00rootroot00000000000000// 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.24.1/scripts/sync_headers.sh000077500000000000000000000153131506776124200200760ustar00rootroot00000000000000#!/usr/bin/env bash # Syncs headers from a local kernel repository. # # This script attempts to find the transitive closure over the C headers that # are generally needed for BPF programs. This is not an easy task: headers can # include other headers based on `#define`s (e.g. whether `__KERNEL__` is set) # and these defines are often set for BPF programs, and headers can include # different headers depend on the architecture. This script is fundamentally # fragile, and contains hacks in an attempt to do the right thing. This script # should improve over time as these special cases are identified, and as our # requirements for C interop generally become less ambiguous. # # Please include the kernel commit used when checking in new synced headers. # # Example usage: # # ./scripts/sync_headers.sh ~/my-linux-repo # set -eu shopt -s globstar SCRIPT_NAME=$0 usage() { echo "Usage:" echo " ${SCRIPT_NAME} " echo "" echo " Sync vendored headers for the standard library." } if [[ "$#" -ne 1 ]]; then usage exit 1 fi declare -r LINUX=$1 # The `HEADERS` variable contains a list of general purpose headers that are # known to be important/useful for building the standard library and C # extensions. This is seeded by the set that have been vendored by `libbpf`. # # The key is the header (relative to the include path) and the value is whether # this require is required (failure to include is a failure). declare -A HEADERS HEADERS=( ["linux/bpf_common.h"]=true ["linux/bpf.h"]=true ["linux/btf.h"]=true ["linux/errno.h"]=true ["linux/fcntl.h"]=true ["linux/openat2.h"]=true ["linux/if_link.h"]=true ["linux/if_xdp.h"]=true ["linux/netdev.h"]=true ["linux/netlink.h"]=true ["linux/pkt_cls.h"]=true ["linux/pkt_sched.h"]=true ["linux/perf_event.h"]=true # These are not neccessary found transitively, since it depends on the # current architecture. So add these explicitly. ["linux/byteorder/little_endian.h"]=true ["linux/byteorder/big_endian.h"]=true # These are also not always found transitively. ["linux/kernel.h"]=true ["linux/netdev.h"]=true ["linux/sysinfo.h"]=true # This is not necessarily found transitively, and is not available at all # for certain archiectures. So allow failure for this file. ["asm/posix_types_64.h"]=false # These will end up coming from the non-uapi kernel directory, but may be # included when code is built as `__KERNEL__`. They won't be found be the # automatic transitive search, so we add them manually. ["linux/compiler.h"]=true ["linux/compiler_types.h"]=true ["linux/compiler_attributes.h"]=true ["linux/kasan-checks.h"]=true ["linux/kcsan-checks.h"]=true ["linux/compiler-clang.h"]=true # N.B. only care about clang. ["asm/rwonce.h"]=true ) # When a header is found to be sourced from an `asm/` directory, we will # identify all the other architecture variants that need to be copied. This is # a list of the Linux architecture directories that we will create and check. # Transient headers are subject to the same `required` status as the original. declare -a ARCHES ARCHES=( "x86" "arm" "arm64" "s390" "powerpc" "mips" "riscv" "loongarch" ) function sync() { local -r header=$1 if [[ "$header" =~ asm/.* ]]; then # We are but a single architecture. When copying any architecture-specific # header, we attempt to copy versions from all architectures and put them # in `asm/${arch}` instead. This is then resolved at runtime into the # correct directory. While we build different versions of the binary with # different files, this is a simple enough scheme. See the `ClangBuild` # pass for where this is effectively undone. asmpath="${header##asm/}" for arch in "${ARCHES[@]}"; do if [[ -f "src/stdlib/include/asm/${arch}/${asmpath}" ]]; then # This is done already. Since one assembly header exists, they all must # exist and we don't want to bother doing the transitive check. return 0 fi echo -n "Syncing arch-specific ${header} for ${arch}..." 2>&1 basepath=$(dirname "asm/${arch}/${asmpath}") mkdir -p "src/stdlib/include/${basepath}" # Attempt to copy from the arch-specific directory, but fall back to # the `asm-generic` directory if it is not present there. This is the # case for some files, as these may be merged in the exported version. if ! (cp --preserve=mode "${LINUX}/arch/${arch}/include/uapi/asm/${asmpath}" \ "src/stdlib/include/${basepath}" 2>/dev/null || cp --preserve=mode "${LINUX}/include/uapi/asm-generic/${asmpath}" \ "src/stdlib/include/${basepath}" 2>/dev/null || \ cp --preserve=mode "${LINUX}/include/asm-generic/${asmpath}" \ "src/stdlib/include/${basepath}" 2>/dev/null); then # We can specifically ignore this header. if [[ "${HEADERS[$header]}" != "false" ]]; then echo "failed." return 1 else echo "skipped." fi else echo "done." fi done else # This is a simpler header and is not arch-specific. if [[ -f "src/stdlib/include/${header}" ]]; then return 0 # Done already, see above. fi echo -n "Syncing ${header}..." 2>&1 basepath=$(dirname "${header}") mkdir -p "src/stdlib/include/${basepath}" if ! (cp --preserve=mode "${LINUX}/include/uapi/${header}" \ "src/stdlib/include/${basepath}" 2>/dev/null || \ cp --preserve=mode "${LINUX}/include/${header}" \ "src/stdlib/include/${basepath}" 2>/dev/null); then # We can specifically ignore this header. if [[ "${HEADERS[$header]}" != "false" ]]; then echo "failed." return 1 else echo "skipped." fi else echo "done." fi fi # In order to discover transitive headers required, we run a basic compile # and emit all touched headers. Note that this is run on the **system** # headers, and therefore assumes a stable structure for user headers. # Kernel-specific headers will need to be listed manually above, this step is # effectively best-effort. local -r tmpfile=$(mktemp --tmpdir "XXXXXX.c") echo "#include <$1>" > "${tmpfile}" local nesting local transitive_header clang -H -o /dev/null -c "${tmpfile}" 2>&1 | grep -E '.h$' | \ (while read nesting transitive_header; do local relpath="${transitive_header##*/include/}" sync "${relpath}" done) rm -f "${tmpfile}" } # Wipe existing synced headers. rm -rf \ src/stdlib/include/asm \ src/stdlib/include/asm-* \ src/stdlib/include/linux # Recursively vendor headers. for header in "${!HEADERS[@]}"; do sync "${header}" done bpftrace-0.24.1/scripts/tracepoint_variable_sized_types.py000066400000000000000000000023621506776124200241010ustar00rootroot00000000000000# 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.24.1/scripts/update_codegen_tests.sh000077500000000000000000000001221506776124200216070ustar00rootroot00000000000000#!/bin/bash echo "This script has been deprecated. Use ./tests/codegen-tests.sh" bpftrace-0.24.1/src/000077500000000000000000000000001506776124200141655ustar00rootroot00000000000000bpftrace-0.24.1/src/CMakeLists.txt000066400000000000000000000154511506776124200167330ustar00rootroot00000000000000if(HAVE_BFD_DISASM) set(BFD_DISASM_SRC bfd-disasm.cpp) 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 async_action.cpp 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 probe_matcher.cpp probe_types.cpp procmon.cpp run_bpftrace.cpp usdt.cpp pcap_writer.cpp types_format.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 driver.cpp lockdown.cpp tracepoint_format_parser.cpp ) # So it's not "liblibbpftrace" set_target_properties(libbpftrace PROPERTIES PREFIX "") add_executable(bpftrace main.cpp benchmark.cpp ) # 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) # compile definitions # 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 debugfs output tracefs util) target_link_libraries(runtime ${LIBBPF_LIBRARIES} ${ZLIB_LIBRARIES}) target_link_libraries(libbpftrace parser runtime aot ast arch util cxxdemangler_llvm) 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) if (LIBDW_FOUND) if(STATIC_LINKING) find_package(LibBz2) find_package(LibEbl) add_library(LIBBZ2 STATIC IMPORTED) set_property(TARGET LIBBZ2 PROPERTY IMPORTED_LOCATION ${LIBBZ2_LIBRARIES}) add_library(LIBDW STATIC IMPORTED) set_property(TARGET LIBDW PROPERTY IMPORTED_LOCATION ${LIBDW_LIBRARIES}) set(LIBDW_LIBS LIBBZ2 LIBELF LIBLZMA) if (${LIBEBL_FOUND}) # libebl is not necessary on some systems (e.g. Alpine) add_library(LIBEBL STATIC IMPORTED) set_property(TARGET LIBEBL PROPERTY IMPORTED_LOCATION ${LIBEBL_LIBRARIES}) set(LIBDW_LIBS ${LIBDW_LIBS} LIBEBL) endif() target_link_libraries(LIBDW INTERFACE ${LIBDW_LIBS}) target_link_libraries(runtime LIBDW) else() target_link_libraries(runtime ${LIBDW_LIBRARIES}) endif() endif() # 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 (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(BPFTRACE) add_subdirectory(aot) add_subdirectory(arch) add_subdirectory(ast) add_subdirectory(btf) add_subdirectory(cxxdemangler) add_subdirectory(debugfs) add_subdirectory(output) add_subdirectory(stdlib) add_subdirectory(tracefs) add_subdirectory(util) bpftrace-0.24.1/src/aot/000077500000000000000000000000001506776124200147505ustar00rootroot00000000000000bpftrace-0.24.1/src/aot/CMakeLists.txt000066400000000000000000000022311506776124200175060ustar00rootroot00000000000000add_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 util 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.24.1/src/aot/aot.cpp000066400000000000000000000222571506776124200162470ustar00rootroot00000000000000#include "aot.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "util/paths.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 = util::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 = util::find_near_self(rel.native()); auto path = util::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 (ec) { 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.24.1/src/aot/aot.h000066400000000000000000000006311506776124200157040ustar00rootroot00000000000000#pragma once #include #include "bpftrace.h" #include "required_resources.h" namespace bpftrace::aot { static constexpr std::string_view AOT_SHIM_NAME = "bpftrace-aotrt"; int generate(const RequiredResources &resources, const std::string &out, void *elf, size_t elf_size); int load(BPFtrace &bpftrace, const std::string &in); } // namespace bpftrace::aot bpftrace-0.24.1/src/aot/aot_main.cpp000066400000000000000000000075651506776124200172600ustar00rootroot00000000000000#include #include #include #include #include "aot.h" #include "bpftrace.h" #include "log.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 } int main(int argc, char* argv[]) { std::string output_file, output_format; int c; std::vector named_params; // TODO: which other options from `bpftrace` should be included? const char* const short_opts = "d:f:hVo:qv"; option long_opts[] = { option{ .name = "help", .has_arg = no_argument, .flag = nullptr, .val = 'h', }, option{ .name = "version", .has_arg = no_argument, .flag = nullptr, .val = 'V', }, // Must be last option{ .name = nullptr, .has_arg = 0, .flag = nullptr, .val = 0, }, }; 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::string(optarg) == "libbpf") bt_debug.insert(DebugStage::Libbpf); else if (std::string(optarg) == "verifier") bt_debug.insert(DebugStage::Verifier); else if (std::string(optarg) == "all") { 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; } } while (optind < argc) { auto pos_arg = std::string(argv[optind]); if (pos_arg.starts_with("--")) { named_params.emplace_back(pos_arg.substr(2)); } else { // AOT does not support positional parameters LOG(ERROR) << "AOT does not support positional parameters"; return 1; } optind++; } if (argv[optind]) { usage(std::cerr, argv[0]); return 1; } check_is_root(); libbpf_set_print(libbpf_print); BPFtrace bpftrace; int err = aot::load(bpftrace, argv[0]); if (err) { LOG(ERROR) << "Failed to load AOT script"; return err; } // FIXME(#4087): We should serialize the C enum definitions as part of the AOT // payload in order to allow this printing to work. ast::CDefinitions no_c_defs; return run_bpftrace(bpftrace, output_file, output_format, no_c_defs, bpftrace.bytecode_, std::move(named_params)); } bpftrace-0.24.1/src/arch/000077500000000000000000000000001506776124200151025ustar00rootroot00000000000000bpftrace-0.24.1/src/arch/CMakeLists.txt000066400000000000000000000002001506776124200176320ustar00rootroot00000000000000add_library(arch STATIC arm.cpp ppc64.cpp s390.cpp arch.cpp x86_64.cpp mips64.cpp riscv64.cpp loongarch64.cpp ) bpftrace-0.24.1/src/arch/arch.cpp000066400000000000000000000014311506776124200165220ustar00rootroot00000000000000#include #include "arch/arch.h" namespace bpftrace::arch { std::ostream& operator<<(std::ostream& out, Machine m) { switch (m) { case Machine::X86_64: out << "x86_64"; break; case Machine::ARM: out << "arm"; break; case Machine::ARM64: out << "arm64"; break; case Machine::S390X: out << "s390x"; break; case Machine::PPC64: #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ out << "ppc64le"; #else out << "ppc64"; #endif // __BYTE_ORDER__ break; case Machine::MIPS64: out << "mips64"; break; case Machine::RISCV64: out << "riscv64"; break; case Machine::LOONGARCH64: out << "loongarch64"; break; } return out; } } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/arch.h000066400000000000000000000072771506776124200162050ustar00rootroot00000000000000#pragma once #include #include #include #include namespace bpftrace::arch { // Canonical enum for different architectures. enum Machine { X86_64, ARM, ARM64, S390X, PPC64, MIPS64, RISCV64, LOONGARCH64, }; // Allow printing of the machine type. std::ostream& operator<<(std::ostream& out, Machine m); // In order to ensure that all architecture sources are always compiled, we // define the architecture details as a specific instance of the various // architecture classes. Unused architectures should be stripped out at link // time as those functions are unreferenced. template class Arch { public: constexpr static auto Machine = M; // Returns the assembly name for the architecture. static std::string asm_arch(); // Returns the width of bits in kernel pointers. static size_t kernel_ptr_width(); // Returns additional C definitions that should be applied to compiled code. static const std::vector& c_defs(); // Given a conventional register name, return the expression that should be // used to access this from `struct pt_regs`. This will be dynamically added // and evaluated using the standard type inference mechanisms. static std::optional register_to_pt_regs_expr( const std::string& name); // Returns the offset into the context where this register resides. // // FIXME(#3873): This should be removed in the future. With BTF, there is no // need to statically encode the register offsets and we should instead treat // these as regular field references into the context (e.g. `ctx.ax`). These // field names can then be checked, and will support dynamic reloations, etc. // A pass can be added early to transform `regs("r")` into `ctx.r`, and we // can have proper type inference and type-checking while throwing out code. // However, for now, we retain this method to faciliate the transition. static std::optional register_to_pt_regs_offset( const std::string& name); // Returns the canonical sequence of registers used for the default calling // convention on this architecture. These should be in the form of fields for // `struct pt_regs` (the function above will not be called). static const std::vector& arguments(); // Returns the canonical offset into the stack where arguments start to spill. static size_t argument_stack_offset(); // Returns the canonical register used for the return value on this // architecture, also in the form of a `struct pt_regs` field. static std::string return_value(); // Returns the canonical register used to store the instruction pointer, // also in the form of a `struct pt_regs` field. static std::string pc_value(); // Returns the canonical register used to store the stack pointer, in // the form of a `struct pt_regs` fields. static std::string sp_value(); // Returns the set of valid watchpoint modes. static const std::unordered_set& watchpoint_modes(); }; // Returns the `Machine` for the compiled architecture. constexpr Machine current() { #if defined(__x86_64__) || defined(__amd64__) return Machine::X86_64; #elif defined(__aarch64__) return Machine::ARM64; #elif defined(__arm__) return Machine::ARM; #elif defined(__s390x__) return Machine::S390X; #elif defined(__ppc64__) || defined(__powerpc64__) return Machine::PPC64; #elif defined(__mips64) return Machine::MIPS64; #elif defined(__riscv) && (__riscv_xlen == 64) return Machine::RISCV64; #elif defined(__loongarch64) return Machine::LOONGARCH64; #else #error "Unknown architecture." #endif } // Alias for the current host architecture. using Host = Arch; } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/arm.cpp000066400000000000000000000213011506776124200163620ustar00rootroot00000000000000#include #include "arch.h" namespace bpftrace::arch { template <> std::string Arch::asm_arch() { return "arm"; } template <> size_t Arch::kernel_ptr_width() { return 32; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_arm", }; return defs; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { static const std::unordered_map register_exprs = { { "r0", "uregs[0]" }, { "r1", "uregs[1]" }, { "r2", "uregs[2]" }, { "r3", "uregs[3]" }, { "r4", "uregs[4]" }, { "r5", "uregs[5]" }, { "r6", "uregs[6]" }, { "r7", "uregs[7]" }, { "r8", "uregs[8]" }, { "r9", "uregs[9]" }, { "r10", "uregs[10]" }, // Support the expressions as string literals. { "regs[0]", "uregs[0]" }, { "regs[1]", "uregs[1]" }, { "regs[2]", "uregs[2]" }, { "regs[3]", "uregs[3]" }, { "regs[4]", "uregs[4]" }, { "regs[5]", "uregs[5]" }, { "regs[6]", "uregs[6]" }, { "regs[7]", "uregs[7]" }, { "regs[8]", "uregs[8]" }, { "regs[9]", "uregs[9]" }, { "regs[10]", "uregs[10]" }, // Special registers. { "fp", "uregs[11]" }, { "ip", "uregs[12]" }, { "sp", "uregs[13]" }, { "lr", "uregs[14]" }, { "pc", "uregs[15]" }, { "cpsr", "uregs[16]" }, }; auto it = register_exprs.find(name); if (it != register_exprs.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "r0", 0 }, { "r1", 4 }, { "r2", 8 }, { "r3", 12 }, { "r4", 16 }, { "r5", 20 }, { "r6", 24 }, { "r7", 28 }, { "r8", 32 }, { "r9", 36 }, { "r10", 40 }, // As above, support expressions as string literals. { "regs[0]", 0 }, { "regs[1]", 4 }, { "regs[2]", 8 }, { "regs[3]", 12 }, { "regs[4]", 16 }, { "regs[5]", 20 }, { "regs[6]", 24 }, { "regs[7]", 28 }, { "regs[8]", 32 }, { "regs[9]", 36 }, { "regs[10]", 40 }, // Special registers. { "fp", 44 }, { "ip", 48 }, { "sp", 52 }, { "lr", 56 }, { "pc", 60 }, { "cpsr", 64 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> const std::vector& Arch::arguments() { static std::vector args = { "r0", "r1", "r2", "r3", }; return args; } template <> size_t Arch::argument_stack_offset() { return 0; } template <> std::string Arch::return_value() { return "r0"; } template <> std::string Arch::pc_value() { return "pc"; } template <> std::string Arch::sp_value() { return "sp"; } template <> const std::unordered_set& Arch::watchpoint_modes() { // See arch/arm/kernel/hw_breakpoint.c:arch_build_bp_info in kernel source. static std::unordered_set valid_modes = { "r", "w", "x", "rw", }; return valid_modes; } template <> std::string Arch::asm_arch() { return "arm64"; } template <> size_t Arch::kernel_ptr_width() { return 64; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_arm64", }; return defs; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { static const std::unordered_map register_exprs = { { "r0", "regs[0]" }, { "r1", "regs[1]" }, { "r2", "regs[2]" }, { "r3", "regs[3]" }, { "r4", "regs[4]" }, { "r5", "regs[5]" }, { "r6", "regs[6]" }, { "r7", "regs[7]" }, { "r8", "regs[8]" }, { "r9", "regs[9]" }, { "r10", "regs[10]" }, { "r11", "regs[11]" }, { "r12", "regs[12]" }, { "r13", "regs[13]" }, { "r14", "regs[14]" }, { "r15", "regs[15]" }, { "r16", "regs[16]" }, { "r17", "regs[17]" }, { "r18", "regs[18]" }, { "r19", "regs[19]" }, { "r20", "regs[20]" }, { "r21", "regs[21]" }, { "r22", "regs[22]" }, { "r23", "regs[23]" }, { "r24", "regs[24]" }, { "r25", "regs[25]" }, { "r26", "regs[26]" }, { "r27", "regs[27]" }, { "r28", "regs[28]" }, { "r29", "regs[29]" }, { "r30", "regs[30]" }, // Support expressions as string literals. { "regs[0]", "regs[0]" }, { "regs[1]", "regs[1]" }, { "regs[2]", "regs[2]" }, { "regs[3]", "regs[3]" }, { "regs[4]", "regs[4]" }, { "regs[5]", "regs[5]" }, { "regs[6]", "regs[6]" }, { "regs[7]", "regs[7]" }, { "regs[8]", "regs[8]" }, { "regs[9]", "regs[9]" }, { "regs[10]", "regs[10]" }, { "regs[11]", "regs[11]" }, { "regs[12]", "regs[12]" }, { "regs[13]", "regs[13]" }, { "regs[14]", "regs[14]" }, { "regs[15]", "regs[15]" }, { "regs[16]", "regs[16]" }, { "regs[17]", "regs[17]" }, { "regs[18]", "regs[18]" }, { "regs[19]", "regs[19]" }, { "regs[20]", "regs[20]" }, { "regs[21]", "regs[21]" }, { "regs[22]", "regs[22]" }, { "regs[23]", "regs[23]" }, { "regs[24]", "regs[24]" }, { "regs[25]", "regs[25]" }, { "regs[26]", "regs[26]" }, { "regs[27]", "regs[27]" }, { "regs[28]", "regs[28]" }, { "regs[29]", "regs[29]" }, { "regs[30]", "regs[30]" }, // Compat registers for 32-bit userspace. { "compat_fp", "regs[11]", }, { "compat_sp", "regs[13]", }, { "compat_lr", "regs[14]" }, // Special registers. { "sp", "sp" }, { "pc", "pc" }, { "pstate", "pstate" }, }; auto it = register_exprs.find(name); if (it != register_exprs.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "r0", 0 }, { "r1", 8 }, { "r2", 16 }, { "r3", 24 }, { "r4", 32 }, { "r5", 40 }, { "r6", 48 }, { "r7", 56 }, { "r8", 64 }, { "r9", 72 }, { "r10", 80 }, { "r11", 88 }, { "r12", 96 }, { "r13", 104 }, { "r14", 112 }, { "r15", 120 }, { "r16", 128 }, { "r17", 136 }, { "r18", 144 }, { "r19", 152 }, { "r20", 160 }, { "r21", 168 }, { "r22", 176 }, { "r23", 184 }, { "r24", 192 }, { "r25", 200 }, { "r26", 208 }, { "r27", 216 }, { "r28", 224 }, { "r29", 232 }, { "r30", 240 }, // Full expressions as string literals. { "regs[0]", 0 }, { "regs[1]", 8 }, { "regs[2]", 16 }, { "regs[3]", 24 }, { "regs[4]", 32 }, { "regs[5]", 40 }, { "regs[6]", 48 }, { "regs[7]", 56 }, { "regs[8]", 64 }, { "regs[9]", 72 }, { "regs[10]", 80 }, { "regs[11]", 88 }, { "regs[12]", 96 }, { "regs[13]", 104 }, { "regs[14]", 112 }, { "regs[15]", 120 }, { "regs[16]", 128 }, { "regs[17]", 136 }, { "regs[18]", 144 }, { "regs[19]", 152 }, { "regs[20]", 160 }, { "regs[21]", 168 }, { "regs[22]", 176 }, { "regs[23]", 184 }, { "regs[24]", 192 }, { "regs[25]", 200 }, { "regs[26]", 208 }, { "regs[27]", 216 }, { "regs[28]", 224 }, { "regs[29]", 232 }, { "regs[30]", 240 }, // Compat registers for 32-bit userspace. { "compat_fp", 88 }, { "compat_sp", 104 }, { "compat_lr", 112 }, // Special registers. { "sp", 248 }, { "pc", 256 }, { "pstate", 264 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> const std::vector& Arch::arguments() { static std::vector args = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", }; return args; } template <> size_t Arch::argument_stack_offset() { return 0; } template <> std::string Arch::return_value() { return "r0"; } template <> std::string Arch::pc_value() { return "pc"; } template <> std::string Arch::sp_value() { return "sp"; } template <> const std::unordered_set& Arch::watchpoint_modes() { // See arch/arm/kernel/hw_breakpoint.c:arch_build_bp_info in kernel source. static std::unordered_set valid_modes = { "r", "w", "x", "rw", }; return valid_modes; } } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/loongarch64.cpp000066400000000000000000000123571506776124200177440ustar00rootroot00000000000000#include #include "arch.h" namespace bpftrace::arch { template <> std::string Arch::asm_arch() { return "loongarch"; } template <> size_t Arch::kernel_ptr_width() { return 64; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_loongarch", }; return defs; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { static const std::unordered_map register_exprs = { { "r0", "regs[0]" }, { "r1", "regs[1]" }, { "r2", "regs[2]" }, { "r3", "regs[3]" }, { "r4", "regs[4]" }, { "r5", "regs[5]" }, { "r6", "regs[6]" }, { "r7", "regs[7]" }, { "r8", "regs[8]" }, { "r9", "regs[9]" }, { "r10", "regs[10]" }, { "r11", "regs[11]" }, { "r12", "regs[12]" }, { "r13", "regs[13]" }, { "r14", "regs[14]" }, { "r15", "regs[15]" }, { "r16", "regs[16]" }, { "r17", "regs[17]" }, { "r18", "regs[18]" }, { "r19", "regs[19]" }, { "r20", "regs[20]" }, { "r21", "regs[21]" }, { "r22", "regs[22]" }, { "r23", "regs[23]" }, { "r24", "regs[24]" }, { "r25", "regs[25]" }, { "r26", "regs[26]" }, { "r27", "regs[27]" }, { "r28", "regs[28]" }, { "r29", "regs[29]" }, { "r30", "regs[30]" }, { "r31", "regs[31]" }, // Support full expressions as string literals. { "regs[0]", "regs[0]" }, { "regs[1]", "regs[1]" }, { "regs[2]", "regs[2]" }, { "regs[3]", "regs[3]" }, { "regs[4]", "regs[4]" }, { "regs[5]", "regs[5]" }, { "regs[6]", "regs[6]" }, { "regs[7]", "regs[7]" }, { "regs[8]", "regs[8]" }, { "regs[9]", "regs[9]" }, { "regs[10]", "regs[10]" }, { "regs[11]", "regs[11]" }, { "regs[12]", "regs[12]" }, { "regs[13]", "regs[13]" }, { "regs[14]", "regs[14]" }, { "regs[15]", "regs[15]" }, { "regs[16]", "regs[16]" }, { "regs[17]", "regs[17]" }, { "regs[18]", "regs[18]" }, { "regs[19]", "regs[19]" }, { "regs[20]", "regs[20]" }, { "regs[21]", "regs[21]" }, { "regs[22]", "regs[22]" }, { "regs[23]", "regs[23]" }, { "regs[24]", "regs[24]" }, { "regs[25]", "regs[25]" }, { "regs[26]", "regs[26]" }, { "regs[27]", "regs[27]" }, { "regs[28]", "regs[28]" }, { "regs[29]", "regs[29]" }, { "regs[30]", "regs[30]" }, { "regs[31]", "regs[31]" }, // Special registers. { "orig_a0", "orig_a0" }, { "pc", "csr_era" }, }; auto it = register_exprs.find(name); if (it != register_exprs.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "r0", 0 }, { "r1", 8 }, { "r2", 16 }, { "r3", 24 }, { "r4", 32 }, { "r5", 40 }, { "r6", 48 }, { "r7", 56 }, { "r8", 64 }, { "r9", 72 }, { "r10", 80 }, { "r11", 88 }, { "r12", 96 }, { "r13", 104 }, { "r14", 112 }, { "r15", 120 }, { "r16", 128 }, { "r17", 136 }, { "r18", 144 }, { "r19", 152 }, { "r20", 160 }, { "r21", 168 }, { "r22", 176 }, { "r23", 184 }, { "r24", 192 }, { "r25", 200 }, { "r26", 208 }, { "r27", 216 }, { "r28", 224 }, { "r29", 232 }, { "r30", 240 }, { "r31", 248 }, // Support full expressions as string literals. { "regs[0]", 0 }, { "regs[1]", 8 }, { "regs[2]", 16 }, { "regs[3]", 24 }, { "regs[4]", 32 }, { "regs[5]", 40 }, { "regs[6]", 48 }, { "regs[7]", 56 }, { "regs[8]", 64 }, { "regs[9]", 72 }, { "regs[10]", 80 }, { "regs[11]", 88 }, { "regs[12]", 96 }, { "regs[13]", 104 }, { "regs[14]", 112 }, { "regs[15]", 120 }, { "regs[16]", 128 }, { "regs[17]", 136 }, { "regs[18]", 144 }, { "regs[19]", 152 }, { "regs[20]", 160 }, { "regs[21]", 168 }, { "regs[22]", 176 }, { "regs[23]", 184 }, { "regs[24]", 192 }, { "regs[25]", 200 }, { "regs[26]", 208 }, { "regs[27]", 216 }, { "regs[28]", 224 }, { "regs[29]", 232 }, { "regs[30]", 240 }, { "regs[31]", 248 }, // Special registers. { "orig_a0", 256 }, { "pc", 264 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> const std::vector& Arch::arguments() { static std::vector args = { "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", }; return args; } template <> size_t Arch::argument_stack_offset() { return 0; } template <> std::string Arch::return_value() { return "r4"; } template <> std::string Arch::pc_value() { return "pc"; } template <> std::string Arch::sp_value() { return "r3"; } template <> const std::unordered_set& Arch< Machine::LOONGARCH64>::watchpoint_modes() { static std::unordered_set valid_modes = {}; return valid_modes; } } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/mips64.cpp000066400000000000000000000126221506776124200167330ustar00rootroot00000000000000#include #include "arch.h" namespace bpftrace::arch { template <> std::string Arch::asm_arch() { return "mips"; } template <> size_t Arch::kernel_ptr_width() { return 64; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_mips", }; return defs; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { static const std::unordered_map register_exprs = { { "zero", "regs[0]" }, { "at", "regs[1]" }, { "v0", "regs[2]" }, { "v1", "regs[3]" }, { "a0", "regs[4]" }, { "a1", "regs[5]" }, { "a2", "regs[6]" }, { "a3", "regs[7]" }, { "a4", "regs[8]" }, { "a5", "regs[9]" }, { "a6", "regs[10]" }, { "a7", "regs[11]" }, { "t0", "regs[12]" }, { "t1", "regs[13]" }, { "t2", "regs[14]" }, { "t3", "regs[15]" }, { "s0", "regs[16]" }, { "s1", "regs[17]" }, { "s2", "regs[18]" }, { "s3", "regs[19]" }, { "s4", "regs[20]" }, { "s5", "regs[21]" }, { "s6", "regs[22]" }, { "s7", "regs[23]" }, { "t8", "regs[24]" }, { "t9", "regs[25]" }, { "k0", "regs[26]" }, { "k1", "regs[27]" }, { "gp", "regs[28]" }, { "sp", "regs[29]" }, { "fp", "regs[30]" }, { "fp/s8", "regs[30]" }, { "ra", "regs[31]" }, // Support full expressions as string literals. { "regs[0]", "regs[0]" }, { "regs[1]", "regs[1]" }, { "regs[2]", "regs[2]" }, { "regs[3]", "regs[3]" }, { "regs[4]", "regs[4]" }, { "regs[5]", "regs[5]" }, { "regs[6]", "regs[6]" }, { "regs[7]", "regs[7]" }, { "regs[8]", "regs[8]" }, { "regs[9]", "regs[9]" }, { "regs[10]", "regs[10]" }, { "regs[11]", "regs[11]" }, { "regs[12]", "regs[12]" }, { "regs[13]", "regs[13]" }, { "regs[14]", "regs[14]" }, { "regs[15]", "regs[15]" }, { "regs[16]", "regs[16]" }, { "regs[17]", "regs[17]" }, { "regs[18]", "regs[18]" }, { "regs[19]", "regs[19]" }, { "regs[20]", "regs[20]" }, { "regs[21]", "regs[21]" }, { "regs[22]", "regs[22]" }, { "regs[23]", "regs[23]" }, { "regs[24]", "regs[24]" }, { "regs[25]", "regs[25]" }, { "regs[26]", "regs[26]" }, { "regs[27]", "regs[27]" }, { "regs[28]", "regs[28]" }, { "regs[29]", "regs[29]" }, { "regs[30]", "regs[30]" }, { "regs[31]", "regs[31]" }, // System registers. { "cp0_status", "cp0_status" }, { "hi", "hi" }, { "lo", "lo" }, { "cp0_badvaddr", "cp0_badvaddr" }, { "cp0_cause", "cp0_cause" }, { "cp0_epc", "cp0_epc" }, }; auto it = register_exprs.find(name); if (it != register_exprs.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "zero", 0 }, { "at", 8 }, { "v0", 16 }, { "v1", 24 }, { "a0", 32 }, { "a1", 40 }, { "a2", 48 }, { "a3", 56 }, { "a4", 64 }, { "a5", 72 }, { "a6", 80 }, { "a7", 88 }, { "t0", 96 }, { "t1", 104 }, { "t2", 112 }, { "t3", 120 }, { "s0", 128 }, { "s1", 136 }, { "s2", 144 }, { "s3", 152 }, { "s4", 160 }, { "s5", 168 }, { "s6", 176 }, { "s7", 184 }, { "t8", 192 }, { "t9", 200 }, { "k0", 208 }, { "k1", 216 }, { "gp", 224 }, { "sp", 232 }, { "fp", 240 }, { "fp/s8", 240 }, { "ra", 248 }, // Support full expressions as literals. { "regs[0]", 0 }, { "regs[1]", 8 }, { "regs[2]", 16 }, { "regs[3]", 24 }, { "regs[4]", 32 }, { "regs[5]", 40 }, { "regs[6]", 48 }, { "regs[7]", 56 }, { "regs[8]", 64 }, { "regs[9]", 72 }, { "regs[10]", 80 }, { "regs[11]", 88 }, { "regs[12]", 96 }, { "regs[13]", 104 }, { "regs[14]", 112 }, { "regs[15]", 120 }, { "regs[16]", 128 }, { "regs[17]", 136 }, { "regs[18]", 144 }, { "regs[19]", 152 }, { "regs[20]", 160 }, { "regs[21]", 168 }, { "regs[22]", 176 }, { "regs[23]", 184 }, { "regs[24]", 192 }, { "regs[25]", 200 }, { "regs[26]", 208 }, { "regs[27]", 216 }, { "regs[28]", 224 }, { "regs[29]", 232 }, { "regs[30]", 240 }, { "regs[31]", 248 }, // System registers. { "cp0_status", 256 }, { "hi", 264 }, { "lo", 272 }, { "cp0_badvaddr", 280 }, { "cp0_cause", 288 }, { "cp0_epc", 296 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> const std::vector& Arch::arguments() { static std::vector args = { "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", }; return args; } template <> size_t Arch::argument_stack_offset() { return 8; } template <> std::string Arch::return_value() { return "v0"; } template <> std::string Arch::pc_value() { return "cp0_epc"; } template <> std::string Arch::sp_value() { return "sp"; } template <> const std::unordered_set& Arch::watchpoint_modes() { static std::unordered_set valid_modes = {}; return valid_modes; } } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/ppc64.cpp000066400000000000000000000077161506776124200165550ustar00rootroot00000000000000#include #include "arch.h" namespace bpftrace::arch { template <> std::string Arch::asm_arch() { return "powerpc"; } template <> size_t Arch::kernel_ptr_width() { return 64; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_powerpc", }; return defs; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { // PowerPC pt_regs structure field mapping static const std::unordered_map register_exprs = { { "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", "nip" }, { "msr", "msr" }, { "orig_gpr3", "orig_gpr3" }, { "ctr", "ctr" }, { "link", "link" }, { "xer", "xer" }, { "ccr", "ccr" }, { "softe", "softe" }, { "trap", "trap" }, { "dar", "dar" }, { "dsisr", "dsisr" }, { "result", "result" }, }; auto it = register_exprs.find(name); if (it != register_exprs.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "r0", 0 }, { "r1", 8 }, { "r2", 16 }, { "r3", 24 }, { "r4", 32 }, { "r5", 40 }, { "r6", 48 }, { "r7", 56 }, { "r8", 64 }, { "r9", 72 }, { "r10", 80 }, { "r11", 88 }, { "r12", 96 }, { "r13", 104 }, { "r14", 112 }, { "r15", 120 }, { "r16", 128 }, { "r17", 136 }, { "r18", 144 }, { "r19", 152 }, { "r20", 160 }, { "r21", 168 }, { "r22", 176 }, { "r23", 184 }, { "r24", 192 }, { "r25", 200 }, { "r26", 208 }, { "r27", 216 }, { "r28", 224 }, { "r29", 232 }, { "r30", 240 }, { "r31", 248 }, { "nip", 256 }, { "msr", 264 }, { "orig_gpr3", 272 }, { "ctr", 280 }, { "link", 288 }, { "xer", 296 }, { "ccr", 304 }, { "softe", 312 }, { "trap", 320 }, { "dar", 328 }, { "dsisr", 336 }, { "result", 344 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> const std::vector& Arch::arguments() { static std::vector args = { "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", }; return args; } template <> size_t Arch::argument_stack_offset() { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return 96; // Little endian: sp + 32 + 8 regs save area + argX #else return 112; // Big endian: sp + 48 + 8 regs save area + argX #endif // __BYTE_ORDER__ } template <> std::string Arch::return_value() { return "r3"; } template <> std::string Arch::pc_value() { return "nip"; } template <> std::string Arch::sp_value() { return "r1"; } template <> const std::unordered_set& Arch::watchpoint_modes() { // See PowerISA Book III v3.1B, Section 5.4.4 and 10.4. static std::unordered_set valid_modes = { "r", "w", "rw", }; return valid_modes; } } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/riscv64.cpp000066400000000000000000000055261506776124200171160ustar00rootroot00000000000000#include #include "arch/arch.h" namespace bpftrace::arch { template <> std::string Arch::asm_arch() { return "riscv"; } template <> size_t Arch::kernel_ptr_width() { return 64; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_riscv", }; return defs; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { static const std::unordered_map register_exprs = { { "pc", "epc" }, { "ra", "ra" }, { "sp", "sp" }, { "gp", "gp" }, { "tp", "tp" }, { "t0", "t0" }, { "t1", "t1" }, { "t2", "t2" }, { "s0", "s0" }, { "s1", "s1" }, { "a0", "a0" }, { "a1", "a1" }, { "a2", "a2" }, { "a3", "a3" }, { "a4", "a4" }, { "a5", "a5" }, { "a6", "a6" }, { "a7", "a7" }, { "s2", "s2" }, { "s3", "s3" }, { "s4", "s4" }, { "s5", "s5" }, { "s6", "s6" }, { "s7", "s7" }, { "s8", "s8" }, { "s9", "s9" }, { "s10", "s10" }, { "s11", "s11" }, { "t3", "t3" }, { "t4", "t4" }, { "t5", "t5" }, { "t6", "t6" }, }; auto it = register_exprs.find(name); if (it != register_exprs.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "pc", 0 }, { "ra", 8 }, { "sp", 16 }, { "gp", 24 }, { "tp", 32 }, { "t0", 40 }, { "t1", 48 }, { "t2", 56 }, { "s0", 64 }, { "s1", 72 }, { "a0", 80 }, { "a1", 88 }, { "a2", 96 }, { "a3", 104 }, { "a4", 112 }, { "a5", 120 }, { "a6", 128 }, { "a7", 136 }, { "s2", 144 }, { "s3", 152 }, { "s4", 160 }, { "s5", 168 }, { "s6", 176 }, { "s7", 184 }, { "s8", 192 }, { "s9", 200 }, { "s10", 208 }, { "s11", 216 }, { "t3", 224 }, { "t4", 232 }, { "t5", 240 }, { "t6", 248 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> const std::vector& Arch::arguments() { static std::vector args = { "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", }; return args; } template <> size_t Arch::argument_stack_offset() { return 0; } template <> std::string Arch::return_value() { return "a0"; } template <> std::string Arch::pc_value() { return "pc"; } template <> std::string Arch::sp_value() { return "sp"; } template <> const std::unordered_set& Arch< Machine::RISCV64>::watchpoint_modes() { static std::unordered_set valid_modes = {}; return valid_modes; } } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/s390.cpp000066400000000000000000000052441506776124200163110ustar00rootroot00000000000000#include #include "arch.h" namespace bpftrace::arch { template <> std::string Arch::asm_arch() { return "s390"; } template <> size_t Arch::kernel_ptr_width() { return 64; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_s390", }; return defs; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { static const std::unordered_map register_exprs = { { "arg", "args[0]" }, { "pswmask", "psw.mask" }, { "pswaddr", "psw.addr" }, { "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]" }, }; auto it = register_exprs.find(name); if (it != register_exprs.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "arg", 0 }, { "pswmask", 8 }, { "pswaddr", 16 }, { "r0", 24 }, { "r1", 32 }, { "r2", 40 }, { "r3", 48 }, { "r4", 56 }, { "r5", 64 }, { "r6", 72 }, { "r7", 80 }, { "r8", 88 }, { "r9", 96 }, { "r10", 104 }, { "r11", 112 }, { "r12", 120 }, { "r13", 128 }, { "r14", 136 }, { "r15", 144 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> const std::vector& Arch::arguments() { static std::vector args = { "r2", "r3", "r4", "r5", "r6", }; return args; } template <> size_t Arch::argument_stack_offset() { // For s390x, r2-r6 registers are used as function arguments, then the extra // arguments can be found starting at sp+160. return 160; } template <> std::string Arch::return_value() { return "r2"; } template <> std::string Arch::pc_value() { return "pswaddr"; } template <> std::string Arch::sp_value() { return "r15"; } template <> const std::unordered_set& Arch::watchpoint_modes() { static std::unordered_set valid_modes = {}; return valid_modes; } } // namespace bpftrace::arch bpftrace-0.24.1/src/arch/x86_64.cpp000066400000000000000000000047061506776124200165530ustar00rootroot00000000000000#include #include "arch.h" namespace bpftrace::arch { template <> std::string Arch::asm_arch() { return "x86"; } template <> size_t Arch::kernel_ptr_width() { return 64; } template <> const std::vector& Arch::c_defs() { static std::vector defs = { "__TARGET_ARCH_x86", }; return defs; } template <> std::optional Arch::register_to_pt_regs_offset( const std::string& name) { static const std::unordered_map register_offsets = { { "r15", 0 }, { "r14", 8 }, { "r13", 16 }, { "r12", 24 }, { "bp", 32 }, { "bx", 40 }, { "r11", 48 }, { "r10", 56 }, { "r9", 64 }, { "r8", 72 }, { "ax", 80 }, { "cx", 88 }, { "dx", 96 }, { "si", 104 }, { "di", 112 }, { "orig_rax", 120 }, { "ip", 128 }, { "cs", 136 }, { "flags", 144 }, { "sp", 152 }, { "ss", 160 }, }; auto it = register_offsets.find(name); if (it != register_offsets.end()) { return it->second; } return std::nullopt; } template <> std::optional Arch::register_to_pt_regs_expr( const std::string& name) { // All the registers in x86-64 are defined against the existing pt_regs // struct, and there are no aliases. In the future, we could define aliases // like the `rXX` variants as opposed to `ax`, `bx`, etc. or we could add // aliases like `fp` for `bp`. auto offset = register_to_pt_regs_offset(name); if (!offset) { return std::nullopt; } return name; } template <> const std::vector& Arch::arguments() { static std::vector args = { "di", "si", "dx", "cx", "r8", "r9", }; return args; } template <> size_t Arch::argument_stack_offset() { // The return address is pushed on the frame, so we need to reach up // the stack further to find the first argument. return 8; } template <> std::string Arch::return_value() { return "ax"; } template <> std::string Arch::pc_value() { return "ip"; } template <> std::string Arch::sp_value() { return "sp"; } template <> const std::unordered_set& Arch::watchpoint_modes() { // See intel developer manual, Volume 3, section 17.2.4. static std::unordered_set valid_modes = { "rw", "w", "x", }; return valid_modes; } } // namespace bpftrace::arch bpftrace-0.24.1/src/ast/000077500000000000000000000000001506776124200147545ustar00rootroot00000000000000bpftrace-0.24.1/src/ast/CMakeLists.txt000066400000000000000000000055421506776124200175220ustar00rootroot00000000000000add_library(ast_defs STATIC ast.cpp context.cpp helpers.cpp location.cpp ) add_dependencies(ast_defs parser) target_link_libraries(ast_defs util) add_library(ast STATIC async_event_types.cpp attachpoint_parser.cpp codegen_helper.cpp diagnostic.cpp dibuilderbpf.cpp irbuilderbpf.cpp pass_manager.cpp signal.cpp passes/c_macro_expansion.cpp passes/clang_parser.cpp passes/clang_build.cpp passes/named_param.cpp passes/codegen_resources.cpp passes/config_analyser.cpp passes/deprecated.cpp passes/field_analyser.cpp passes/fold_literals.cpp passes/import_scripts.cpp passes/link.cpp passes/map_sugar.cpp passes/macro_expansion.cpp passes/pid_filter_pass.cpp passes/portability_analyser.cpp passes/printer.cpp passes/probe_expansion.cpp passes/probe_prune.cpp passes/resource_analyser.cpp passes/semantic_analyser.cpp passes/codegen_llvm.cpp passes/resolve_imports.cpp passes/return_path_analyser.cpp passes/recursion_check.cpp passes/type_system.cpp passes/unstable_feature.cpp ) add_dependencies(ast parser) target_compile_definitions(ast PRIVATE ${BPFTRACE_FLAGS}) target_link_libraries(ast PRIVATE debugfs tracefs util stdlib) target_link_libraries(ast PUBLIC ast_defs arch compiler_core parser btf) if (NOT SYSTEM_INCLUDE_PATHS EQUAL "auto") MESSAGE(STATUS "Using SYSTEM_INCLUDE_PATHS=${SYSTEM_INCLUDE_PATHS}") endif() target_compile_definitions(ast PUBLIC SYSTEM_INCLUDE_PATHS="${SYSTEM_INCLUDE_PATHS}") if(STATIC_LINKING) include(Util) 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}" "$") target_link_libraries(ast PUBLIC libclang_static clangDriver clangFrontend clangCodeGen) target_link_libraries(ast PUBLIC ${llvm_libs}) else() if (TARGET LLVM) # 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) else() # some build environment(e.g facebookexperimental/ExtendedAndroidTools) doesn't # have libLLVM.so, drop `USE_SHARED` to avoid forcing link to `-lLLVM`. llvm_config(ast bpfcodegen ipo irreader mcjit orcjit) endif() target_link_libraries(ast PUBLIC libclang clang-cpp) endif() bpftrace-0.24.1/src/ast/ast.cpp000066400000000000000000000244341506776124200162560ustar00rootroot00000000000000#include #include #include "ast/ast.h" #include "ast/context.h" #include "attached_probe.h" #include "log.h" #include "util/int_parser.h" #include "util/strings.h" namespace bpftrace::ast { Diagnostic &Node::addError() const { return state_.diagnostics_->addError(loc); } Diagnostic &Node::addWarning() const { return state_.diagnostics_->addWarning(loc); } const SizedType &Expression::type() const { return std::visit( [](const auto *expr) -> const SizedType & { return expr->type(); }, value); } static constexpr std::string_view ENUM = "enum "; 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 } bool is_comparison_op(Operator op) { switch (op) { case Operator::EQ: case Operator::NE: case Operator::LE: case Operator::GE: case Operator::LT: case Operator::GT: case Operator::LAND: case Operator::LOR: return true; case Operator::PLUS: case Operator::MINUS: case Operator::MUL: case Operator::DIV: case Operator::MOD: case Operator::BAND: case Operator::BOR: case Operator::BXOR: case Operator::LEFT: case Operator::RIGHT: case Operator::INVALID: case Operator::ASSIGN: case Operator::INCREMENT: case Operator::DECREMENT: case Operator::LNOT: case Operator::BNOT: return false; } return false; // 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, Location(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->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 = util::erase_prefix(ap->func); break; case ProbeType::fentry: case ProbeType::fexit: { if (match.starts_with("bpf:")) { auto parts = util::split_string(match, ':'); ap->target = parts[0]; auto prog_id = util::to_uint(parts[1]); if (!prog_id) { LOG(BUG) << "Invalid bpf prog id: " << parts[1]; } else { ap->bpf_prog_id = *prog_id; } ap->func = parts[2]; break; } [[fallthrough]]; } case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::tracepoint: case ProbeType::rawtracepoint: // Tracepoint, raw 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 = util::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 = util::erase_prefix(ap->func); ap->ns = util::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; util::erase_prefix(ap->func); break; case ProbeType::software: case ProbeType::hardware: case ProbeType::interval: case ProbeType::profile: case ProbeType::special: case ProbeType::benchmark: case ProbeType::iter: case ProbeType::invalid: break; default: LOG(BUG) << "Unknown probe type"; } return ap; } bool AttachPoint::check_available(const std::string &identifier) const { ProbeType type = probetype(provider); if (identifier == "reg" || identifier == "__builtin_usermode") { 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::benchmark: case ProbeType::tracepoint: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::iter: case ProbeType::rawtracepoint: return false; } } else if (identifier == "uaddr") { switch (type) { case ProbeType::usdt: case ProbeType::uretprobe: case ProbeType::uprobe: return true; case ProbeType::invalid: case ProbeType::special: case ProbeType::benchmark: 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 (identifier == "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::benchmark: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::iter: return false; } } else if (identifier == "skboutput" || identifier == "socket_cookie") { return bpftrace::progtype(type) == libbpf::BPF_PROG_TYPE_TRACING; } if (type == ProbeType::invalid) return false; return true; } std::string AttachPoint::name() const { std::string n = provider; if (!target.empty()) n += ":" + target; if (!lang.empty()) n += ":" + lang; if (!ns.empty()) n += ":" + ns; if (bpf_prog_id != 0) n += ":" + std::to_string(bpf_prog_id); if (!func.empty()) { 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.empty()) n += ":" + mode; return n; } int AttachPoint::index() const { return index_; } void AttachPoint::set_index(int index) { index_ = index; } std::string Probe::args_typename() const { return "struct " + orig_name + "_args"; } int Probe::index() const { return index_; } void Probe::set_index(int index) { index_ = index; } bool Probe::has_ap_of_probetype(ProbeType probe_type) { return std::ranges::any_of(attach_points, [probe_type](auto *ap) { return probetype(ap->provider) == probe_type; }); } void Program::clear_empty_probes() { auto it = std::ranges::remove_if(probes.begin(), probes.end(), [](const Probe *p) { return p->attach_points.empty(); }); probes.erase(it.begin(), it.end()); } SizedType ident_to_record(const std::string &ident, int pointer_level) { SizedType result = CreateRecord(ident); 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.24.1/src/ast/ast.h000066400000000000000000001220321506776124200157140ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include "ast/clone.h" #include "ast/context.h" #include "diagnostic.h" #include "probe_types.h" #include "types.h" #include "usdt.h" #include "util/strings.h" namespace bpftrace::ast { 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, }; class Node { public: Node(ASTContext &ctx, Location &&loc) : state_(*ctx.state_), loc(loc) {}; virtual ~Node() = default; Node(const Node &) = delete; Node &operator=(const Node &) = delete; Node(Node &&) = delete; Node &operator=(Node &&) = delete; Diagnostic &addError() const; Diagnostic &addWarning() const; private: // N.B. it is not legal to hold on to a long-term reference to `ASTContext&`, // as this is generally movable. Therefore, we hold on to the internal state // only, which will not be moving. // // See `ASTContext::State` for more information. ASTContext::State &state_; public: // This is temporarily accessible by other classes because we don't have a // clear `clone` operation at this time. Eventually this should be made // private and we should rely on a clear model for cloning nodes. Location loc; }; template class VariantNode { public: template VariantNode(T *value) requires(std::is_same_v || ...) : value(value){}; template bool is() const { return std::holds_alternative(value); } template T *as() const { if (is()) { return std::get(value); } return nullptr; } Node &node() const { return std::visit([](auto *v) -> Node & { return *v; }, value); } const Location &loc() const { return std::visit([](const auto *v) -> const Location & { return v->loc; }, value); } std::variant value; }; class Integer; class NegativeInteger; class Boolean; class PositionalParameter; class PositionalParameterCount; class String; class Identifier; class Builtin; class Call; class Sizeof; class Offsetof; class Map; class Variable; class VariableAddr; class MapAddr; class Binop; class Unop; class FieldAccess; class ArrayAccess; class TupleAccess; class MapAccess; class Cast; class Tuple; class Ternary; class BlockExpr; class Expression : public VariantNode { public: using VariantNode::VariantNode; Expression() : Expression(static_cast(nullptr)) {}; // The `type` method is the only common thing required by all expression // types. This will on the variant types. const SizedType &type() const; }; using ExpressionList = std::vector; class ExprStatement; class VarDeclStatement; class AssignScalarMapStatement; class AssignMapStatement; class AssignVarStatement; class If; class Unroll; class Jump; class While; class For; class Block; class Statement : public VariantNode { public: using VariantNode::VariantNode; Statement() : Statement(static_cast(nullptr)) {}; }; using StatementList = std::vector; class Macro; class MapDeclStatement; class Probe; class Subprog; class RootStatement : public VariantNode { public: using VariantNode::VariantNode; RootStatement() : RootStatement(static_cast(nullptr)) {}; }; using RootStatements = std::vector; class Integer : public Node { public: explicit Integer(ASTContext &ctx, uint64_t n, Location &&loc, bool force_unsigned = false) : Node(ctx, std::move(loc)), integer_type(force_unsigned || n > std::numeric_limits::max() ? CreateUInt64() : CreateInt64()), value(n) {}; explicit Integer(ASTContext &ctx, const Integer &other, const Location &loc) : Node(ctx, loc + other.loc), integer_type(other.integer_type), value(other.value) {}; const SizedType &type() const { return integer_type; } // This literal has a dynamic type, but it is not mutable. The type is // generally signed if the signed value is capable of holding the literal, // otherwise it is unsigned. This is the existing convention. // // However, the `force_unsigned` parameter can override this. This can be // used for small cases that are explicitly unsigned (e.g. `sizeof`), and is // preserved when folding literals in order to provide the intuitive type. const SizedType integer_type; const uint64_t value; }; class NegativeInteger : public Node { public: explicit NegativeInteger(ASTContext &ctx, int64_t n, Location &&loc) : Node(ctx, std::move(loc)), value(n) {}; explicit NegativeInteger(ASTContext &ctx, const NegativeInteger &other, const Location &loc) : Node(ctx, loc + other.loc), value(other.value) {}; const SizedType &type() const { static SizedType int64 = CreateInt64(); return int64; } const int64_t value; }; class Boolean : public Node { public: explicit Boolean(ASTContext &ctx, bool val, Location &&loc) : Node(ctx, std::move(loc)), value(val) {}; explicit Boolean(ASTContext &ctx, const Boolean &other, const Location &loc) : Node(ctx, loc + other.loc), value(other.value) {}; const SizedType &type() const { static SizedType boolean = CreateBool(); return boolean; } const bool value; }; class PositionalParameter : public Node { public: explicit PositionalParameter(ASTContext &ctx, long n, Location &&loc) : Node(ctx, std::move(loc)), n(n) {}; explicit PositionalParameter(ASTContext &ctx, const PositionalParameter &other, const Location &loc) : Node(ctx, loc + other.loc), n(other.n) {}; const SizedType &type() const { static SizedType none = CreateNone(); return none; } const long n; }; class PositionalParameterCount : public Node { public: explicit PositionalParameterCount(ASTContext &ctx, Location &&loc) : Node(ctx, std::move(loc)) {}; explicit PositionalParameterCount( ASTContext &ctx, [[maybe_unused]] const PositionalParameterCount &other, const Location &loc) : Node(ctx, loc + other.loc) {}; const SizedType &type() const { static SizedType none = CreateNone(); return none; } }; class String : public Node { public: explicit String(ASTContext &ctx, std::string str, Location &&loc) : Node(ctx, std::move(loc)), value(std::move(str)), string_type(CreateString(value.size() + 1)) {}; explicit String(ASTContext &ctx, const String &other, const Location &loc) : Node(ctx, loc + other.loc), value(other.value), string_type(other.string_type) {}; const SizedType &type() const { return string_type; } const std::string value; SizedType string_type; }; class Identifier : public Node { public: explicit Identifier(ASTContext &ctx, std::string ident, Location &&loc) : Node(ctx, std::move(loc)), ident(std::move(ident)) {}; explicit Identifier(ASTContext &ctx, const Identifier &other, const Location &loc) : Node(ctx, loc + other.loc), ident(other.ident), ident_type(other.ident_type) {}; const SizedType &type() const { return ident_type; } std::string ident; SizedType ident_type; }; class Builtin : public Node { public: explicit Builtin(ASTContext &ctx, std::string ident, Location &&loc) : Node(ctx, std::move(loc)), ident(std::move(ident)) {}; explicit Builtin(ASTContext &ctx, const Builtin &other, const Location &loc) : Node(ctx, loc + other.loc), ident(other.ident), probe_id(other.probe_id), builtin_type(other.builtin_type) {}; const SizedType &type() const { return builtin_type; } // Check if the builtin is 'arg0' - 'arg255' bool is_argx() const { if (ident.size() < 4 || ident.size() > 6 || !ident.starts_with("arg")) return false; std::string num_part = ident.substr(3); // no leading zeros if (num_part.size() > 1 && num_part.front() == '0') return false; int arg_num = 0; auto [ptr, ec] = std::from_chars(num_part.data(), num_part.data() + num_part.size(), arg_num); return ec == std::errc() && ptr == num_part.data() + num_part.size() && arg_num >= 0 && arg_num < 256; } std::string ident; int probe_id; SizedType builtin_type; }; class Call : public Node { public: explicit Call(ASTContext &ctx, std::string func, Location &&loc) : Node(ctx, std::move(loc)), func(std::move(func)) {}; explicit Call(ASTContext &ctx, std::string func, ExpressionList &&vargs, Location &&loc) : Node(ctx, std::move(loc)), func(std::move(func)), vargs(std::move(vargs)) {}; explicit Call(ASTContext &ctx, const Call &other, const Location &loc) : Node(ctx, loc + other.loc), func(other.func), vargs(clone(ctx, other.vargs, loc)), return_type(other.return_type), injected_args(other.injected_args) {}; const SizedType &type() const { return return_type; } std::string func; ExpressionList vargs; SizedType return_type; // Some passes may inject new arguments to the call, which is always // done at the beginning (in order to support variadic arguments) for // later passes. This is a result of "desugaring" some syntax. When this // happens, this number is increased so that later error reporting can // correctly account for this. size_t injected_args = 0; bool ret_val_discarded = false; }; class Sizeof : public Node { public: explicit Sizeof(ASTContext &ctx, SizedType type, Location &&loc) : Node(ctx, std::move(loc)), record(type) {}; explicit Sizeof(ASTContext &ctx, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), record(expr) {}; explicit Sizeof(ASTContext &ctx, const Sizeof &other, const Location &loc) : Node(ctx, loc + other.loc), record(clone(ctx, other.record, loc)) {}; const SizedType &type() const { // See exception for Integer type construction. static SizedType uint64 = CreateUInt64(); return uint64; } std::variant record; }; class Offsetof : public Node { public: explicit Offsetof(ASTContext &ctx, SizedType record, std::vector &field, Location &&loc) : Node(ctx, std::move(loc)), record(record), field(field) {}; explicit Offsetof(ASTContext &ctx, Expression expr, std::vector &field, Location &&loc) : Node(ctx, std::move(loc)), record(expr), field(field) {}; explicit Offsetof(ASTContext &ctx, const Offsetof &other, const Location &loc) : Node(ctx, loc + other.loc), record(clone(ctx, other.record, loc + other.loc)), field(other.field) {}; const SizedType &type() const { // See exception for Integer type construction. static SizedType uint64 = CreateUInt64(); return uint64; } std::variant record; std::vector field; }; class MapDeclStatement : public Node { public: explicit MapDeclStatement(ASTContext &ctx, std::string ident, std::string bpf_type, int max_entries, Location &&loc) : Node(ctx, std::move(loc)), ident(std::move(ident)), bpf_type(std::move(bpf_type)), max_entries(max_entries) {}; explicit MapDeclStatement(ASTContext &ctx, const MapDeclStatement &other, const Location &loc) : Node(ctx, loc + other.loc), ident(other.ident), bpf_type(other.bpf_type), max_entries(other.max_entries) {}; const std::string ident; const std::string bpf_type; const int max_entries; }; using MapDeclList = std::vector; class Map : public Node { public: explicit Map(ASTContext &ctx, std::string ident, Location &&loc) : Node(ctx, std::move(loc)), ident(std::move(ident)) {}; explicit Map(ASTContext &ctx, const Map &other, const Location &loc) : Node(ctx, loc + other.loc), ident(other.ident), key_type(other.key_type), value_type(other.value_type) {}; const SizedType &type() const { return value_type; } std::string ident; SizedType key_type; SizedType value_type; }; class Variable : public Node { public: explicit Variable(ASTContext &ctx, std::string ident, Location &&loc) : Node(ctx, std::move(loc)), ident(std::move(ident)) {}; explicit Variable(ASTContext &ctx, const Variable &other, const Location &loc) : Node(ctx, loc + other.loc), ident(other.ident), var_type(other.var_type) {}; const SizedType &type() const { return var_type; } std::string ident; SizedType var_type; }; class VariableAddr : public Node { public: explicit VariableAddr(ASTContext &ctx, Variable *var, Location &&loc) : Node(ctx, std::move(loc)), var(var), var_addr_type(CreateNone()) {}; explicit VariableAddr(ASTContext &ctx, const VariableAddr &other, const Location &loc) : Node(ctx, loc + other.loc), var(clone(ctx, other.var, loc)), var_addr_type(other.var_addr_type) {}; const SizedType &type() const { return var_addr_type; } Variable *var = nullptr; SizedType var_addr_type; }; class MapAddr : public Node { public: explicit MapAddr(ASTContext &ctx, Map *map, Location &&loc) : Node(ctx, std::move(loc)), map(map) {}; explicit MapAddr(ASTContext &ctx, const MapAddr &other, const Location &loc) : Node(ctx, loc + other.loc), map(clone(ctx, other.map, loc)) {}; const SizedType &type() const { static SizedType voidptr = CreatePointer(CreateVoid()); return voidptr; } Map *map = nullptr; }; class Binop : public Node { public: explicit Binop(ASTContext &ctx, Expression left, Operator op, Expression right, Location &&loc) : Node(ctx, std::move(loc)), left(std::move(left)), right(std::move(right)), op(op) {}; explicit Binop(ASTContext &ctx, const Binop &other, const Location &loc) : Node(ctx, loc + other.loc), left(clone(ctx, other.left, loc)), right(clone(ctx, other.right, loc)), op(other.op), result_type(other.result_type) {}; const SizedType &type() const { return result_type; } Expression left; Expression right; Operator op; SizedType result_type; }; class Unop : public Node { public: explicit Unop(ASTContext &ctx, Expression expr, Operator op, bool is_post_op, Location &&loc) : Node(ctx, std::move(loc)), expr(std::move(expr)), op(op), is_post_op(is_post_op) {}; explicit Unop(ASTContext &ctx, const Unop &other, const Location &loc) : Node(ctx, loc + other.loc), expr(clone(ctx, other.expr, loc)), op(other.op), is_post_op(other.is_post_op) {}; const SizedType &type() const { return result_type; } Expression expr; Operator op; bool is_post_op; SizedType result_type; }; class FieldAccess : public Node { public: explicit FieldAccess(ASTContext &ctx, Expression expr, std::string field, Location &&loc) : Node(ctx, std::move(loc)), expr(std::move(expr)), field(std::move(field)) {}; explicit FieldAccess(ASTContext &ctx, const FieldAccess &other, const Location &loc) : Node(ctx, loc + other.loc), expr(clone(ctx, other.expr, loc)), field(other.field), field_type(other.field_type) {}; const SizedType &type() const { return field_type; } Expression expr; std::string field; SizedType field_type; }; class ArrayAccess : public Node { public: explicit ArrayAccess(ASTContext &ctx, Expression expr, Expression indexpr, Location &&loc) : Node(ctx, std::move(loc)), expr(std::move(expr)), indexpr(std::move(indexpr)) {}; explicit ArrayAccess(ASTContext &ctx, const ArrayAccess &other, const Location &loc) : Node(ctx, loc + other.loc), expr(clone(ctx, other.expr, loc)), indexpr(clone(ctx, other.indexpr, loc)) {}; const SizedType &type() const { return element_type; } Expression expr; Expression indexpr; SizedType element_type; }; class TupleAccess : public Node { public: explicit TupleAccess(ASTContext &ctx, Expression expr, ssize_t index, Location &&loc) : Node(ctx, std::move(loc)), expr(std::move(expr)), index(index) {}; explicit TupleAccess(ASTContext &ctx, const TupleAccess &other, const Location &loc) : Node(ctx, loc + other.loc), expr(clone(ctx, other.expr, loc)), index(other.index), element_type(other.element_type) {}; const SizedType &type() const { return element_type; } Expression expr; size_t index; SizedType element_type; }; class MapAccess : public Node { public: explicit MapAccess(ASTContext &ctx, Map *map, Expression key, Location &&loc) : Node(ctx, std::move(loc)), map(map), key(std::move(key)) {}; explicit MapAccess(ASTContext &ctx, const MapAccess &other, const Location &loc) : Node(ctx, loc + other.loc), map(clone(ctx, other.map, loc)), key(clone(ctx, other.key, loc)) {}; const SizedType &type() const { return map->type(); } Map *map = nullptr; Expression key; }; class Cast : public Node { public: explicit Cast(ASTContext &ctx, SizedType type, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), cast_type(std::move(type)), expr(std::move(expr)) {}; explicit Cast(ASTContext &ctx, const Cast &other, const Location &loc) : Node(ctx, loc + other.loc), cast_type(other.cast_type), expr(clone(ctx, other.expr, loc)) {}; const SizedType &type() const { return cast_type; } SizedType cast_type; Expression expr; }; class Tuple : public Node { public: explicit Tuple(ASTContext &ctx, ExpressionList &&elems, Location &&loc) : Node(ctx, std::move(loc)), elems(std::move(elems)) {}; explicit Tuple(ASTContext &ctx, const Tuple &other, const Location &loc) : Node(ctx, loc + other.loc), elems(clone(ctx, other.elems, loc)) {}; const SizedType &type() const { return tuple_type; } ExpressionList elems; SizedType tuple_type; }; class ExprStatement : public Node { public: explicit ExprStatement(ASTContext &ctx, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), expr(expr) {}; explicit ExprStatement(ASTContext &ctx, const ExprStatement &other, const Location &loc) : Node(ctx, loc + other.loc), expr(clone(ctx, other.expr, loc)) {}; Expression expr; }; class VarDeclStatement : public Node { public: explicit VarDeclStatement(ASTContext &ctx, Variable *var, SizedType type, Location &&loc) : Node(ctx, std::move(loc)), var(var), type(type) {}; explicit VarDeclStatement(ASTContext &ctx, Variable *var, Location &&loc) : Node(ctx, std::move(loc)), var(var) {}; explicit VarDeclStatement(ASTContext &ctx, const VarDeclStatement &other, const Location &loc) : Node(ctx, loc + other.loc), var(clone(ctx, other.var, loc)), type(other.type) {}; Variable *var = nullptr; std::optional type; }; // Scalar map assignment is purely syntactic sugar that is removed by the pass // returned by`CreateMapSugarPass`. This is replaced by the expansion of // default keys, so that later steps (semantic analysis, code generation), // don't need to worry about this. Maps whose accesses are bound to a single // key will be automatically collapsed into scalar values on the output side. class AssignScalarMapStatement : public Node { public: explicit AssignScalarMapStatement(ASTContext &ctx, Map *map, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), map(map), expr(std::move(expr)) {}; explicit AssignScalarMapStatement(ASTContext &ctx, const AssignScalarMapStatement &other, const Location &loc) : Node(ctx, loc + other.loc), map(clone(ctx, other.map, loc)), expr(clone(ctx, other.expr, loc)) {}; Map *map = nullptr; Expression expr; }; class AssignMapStatement : public Node { public: explicit AssignMapStatement(ASTContext &ctx, Map *map, Expression key, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), map(map), key(std::move(key)), expr(std::move(expr)) {}; explicit AssignMapStatement(ASTContext &ctx, const AssignMapStatement &other, const Location &loc) : Node(ctx, loc + other.loc), map(clone(ctx, other.map, loc)), key(clone(ctx, other.key, loc)), expr(clone(ctx, other.expr, loc)) {}; Map *map = nullptr; Expression key; Expression expr; }; class AssignVarStatement : public Node { public: explicit AssignVarStatement(ASTContext &ctx, Variable *var, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), var_decl(var), expr(std::move(expr)) {}; explicit AssignVarStatement(ASTContext &ctx, VarDeclStatement *var_decl_stmt, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), var_decl(var_decl_stmt), expr(std::move(expr)) {}; explicit AssignVarStatement(ASTContext &ctx, const AssignVarStatement &other, const Location &loc) : Node(ctx, loc + other.loc), var_decl(clone(ctx, other.var_decl, loc)), expr(clone(ctx, other.expr, loc)) {}; Variable *var() { if (std::holds_alternative(var_decl)) { return std::get(var_decl)->var; } else { return std::get(var_decl); } } std::variant var_decl; Expression expr; }; class AssignConfigVarStatement : public Node { public: explicit AssignConfigVarStatement(ASTContext &ctx, std::string var, uint64_t value, Location &&loc) : Node(ctx, std::move(loc)), var(std::move(var)), value(value) {}; explicit AssignConfigVarStatement(ASTContext &ctx, std::string var, std::string value, Location &&loc) : Node(ctx, std::move(loc)), var(std::move(var)), value(std::move(value)) {}; explicit AssignConfigVarStatement(ASTContext &ctx, std::string var, bool value, Location &&loc) : Node(ctx, std::move(loc)), var(std::move(var)), value(value) {}; explicit AssignConfigVarStatement(ASTContext &ctx, const AssignConfigVarStatement &other, const Location &loc) : Node(ctx, loc + other.loc), var(other.var), value(other.value) {}; std::string var; std::variant value; }; using ConfigStatementList = std::vector; class Block : public Node { public: explicit Block(ASTContext &ctx, StatementList &&stmts, Location &&loc) : Node(ctx, std::move(loc)), stmts(std::move(stmts)) {}; explicit Block(ASTContext &ctx, const Block &other, const Location &loc) : Node(ctx, loc + other.loc), stmts(clone(ctx, other.stmts, loc)) {}; const SizedType &type() const { static SizedType none = CreateNone(); return none; } StatementList stmts; }; class BlockExpr : public Node { public: explicit BlockExpr(ASTContext &ctx, StatementList &&stmts, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), stmts(std::move(stmts)), expr(std::move(expr)) {}; explicit BlockExpr(ASTContext &ctx, const BlockExpr &other, const Location &loc) : Node(ctx, loc + other.loc), stmts(clone(ctx, other.stmts, loc)), expr(clone(ctx, other.expr, loc)) {}; const SizedType &type() const { return expr.type(); } StatementList stmts; Expression expr; }; class If : public Node { public: explicit If(ASTContext &ctx, Expression cond, Block *if_block, Block *else_block, Location &&loc) : Node(ctx, std::move(loc)), cond(std::move(cond)), if_block(if_block), else_block(else_block) {}; explicit If(ASTContext &ctx, const If &other, const Location &loc) : Node(ctx, loc + other.loc), cond(clone(ctx, other.cond, loc)), if_block(clone(ctx, other.if_block, loc)), else_block(clone(ctx, other.else_block, loc)) {}; Expression cond; Block *if_block = nullptr; Block *else_block = nullptr; }; class Unroll : public Node { public: explicit Unroll(ASTContext &ctx, Expression expr, Block *block, Location &&loc) : Node(ctx, std::move(loc)), expr(std::move(expr)), block(block) {}; explicit Unroll(ASTContext &ctx, const Unroll &other, const Location &loc) : Node(ctx, loc + other.loc), expr(clone(ctx, other.expr, loc)), block(clone(ctx, other.block, loc)) {}; Expression expr; Block *block = nullptr; }; class Jump : public Node { public: explicit Jump(ASTContext &ctx, JumpType ident, Expression return_value, Location &&loc) : Node(ctx, std::move(loc)), ident(ident), return_value(std::move(return_value)) {}; explicit Jump(ASTContext &ctx, JumpType ident, Location &&loc) : Node(ctx, std::move(loc)), ident(ident) {}; explicit Jump(ASTContext &ctx, const Jump &other, const Location &loc) : Node(ctx, loc + other.loc), ident(other.ident), return_value(clone(ctx, other.return_value, loc)) {}; JumpType ident = JumpType::INVALID; std::optional return_value; }; class Predicate : public Node { public: explicit Predicate(ASTContext &ctx, Expression expr, Location &&loc) : Node(ctx, std::move(loc)), expr(std::move(expr)) {}; explicit Predicate(ASTContext &ctx, const Predicate &other, const Location &loc) : Node(ctx, loc + other.loc), expr(clone(ctx, other.expr, loc)) {}; Expression expr; }; class Ternary : public Node { public: explicit Ternary(ASTContext &ctx, Expression cond, Expression left, Expression right, Location &&loc) : Node(ctx, std::move(loc)), cond(std::move(cond)), left(std::move(left)), right(std::move(right)) {}; explicit Ternary(ASTContext &ctx, const Ternary &other, const Location &loc) : Node(ctx, loc + other.loc), cond(clone(ctx, other.cond, loc)), left(clone(ctx, other.left, loc)), right(clone(ctx, other.right, loc)), result_type(other.result_type) {}; const SizedType &type() const { return result_type; } Expression cond; Expression left; Expression right; SizedType result_type; }; class While : public Node { public: explicit While(ASTContext &ctx, Expression cond, Block *block, Location &&loc) : Node(ctx, std::move(loc)), cond(cond), block(block) {}; explicit While(ASTContext &ctx, const While &other, const Location &loc) : Node(ctx, loc + other.loc), cond(clone(ctx, other.cond, loc)), block(clone(ctx, other.block, loc)) {}; Expression cond; Block *block = nullptr; }; class Range : public Node { public: explicit Range(ASTContext &ctx, Expression start, Expression end, Location &&loc) : Node(ctx, std::move(loc)), start(start), end(end) {}; explicit Range(ASTContext &ctx, const Range &other, const Location &loc) : Node(ctx, loc + other.loc), start(clone(ctx, other.start, loc)), end(clone(ctx, other.end, loc)) {}; Expression start; Expression end; }; class Iterable : public VariantNode { public: using VariantNode::VariantNode; Iterable() : Iterable(static_cast(nullptr)) {}; }; class For : public Node { public: explicit For(ASTContext &ctx, Variable *decl, Map *map, StatementList &&stmts, Location &&loc) : Node(ctx, std::move(loc)), decl(decl), iterable(map), stmts(std::move(stmts)) {}; explicit For(ASTContext &ctx, Variable *decl, Range *range, StatementList &&stmts, Location &&loc) : Node(ctx, std::move(loc)), decl(decl), iterable(range), stmts(std::move(stmts)) {}; explicit For(ASTContext &ctx, const For &other, const Location &loc) : Node(ctx, loc + other.loc), decl(clone(ctx, other.decl, loc)), iterable(clone(ctx, other.iterable, loc)), stmts(clone(ctx, other.stmts, loc)) {}; Variable *decl = nullptr; Iterable iterable; StatementList stmts; SizedType ctx_type; }; class Config : public Node { public: explicit Config(ASTContext &ctx, ConfigStatementList &&stmts, Location &&loc) : Node(ctx, std::move(loc)), stmts(std::move(stmts)) {}; explicit Config(ASTContext &ctx, const Config &other, const Location &loc) : Node(ctx, loc + other.loc), stmts(clone(ctx, other.stmts, loc)) {}; ConfigStatementList stmts; }; class Probe; class AttachPoint : public Node { public: explicit AttachPoint(ASTContext &ctx, std::string raw_input, bool ignore_invalid, Location &&loc) : Node(ctx, std::move(loc)), raw_input(std::move(raw_input)), ignore_invalid(ignore_invalid) {}; explicit AttachPoint(ASTContext &ctx, const AttachPoint &other, const Location &loc) : Node(ctx, loc + other.loc), raw_input(other.raw_input), provider(other.provider), target(other.target), lang(other.lang), ns(other.ns), func(other.func), pin(other.pin), usdt(other.usdt), freq(other.freq), len(other.len), mode(other.mode), async(other.async), address(other.address), func_offset(other.func_offset), ignore_invalid(other.ignore_invalid), index_(other.index_) {}; // 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`. // // FIXME(amscanne): We are not currently cloning AttachPoints correctly, as // they refer to the existing ret probe. // 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 uint64_t address = 0; uint64_t func_offset = 0; uint64_t bpf_prog_id = 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); bool check_available(const std::string &identifier) const; private: int index_ = 0; }; using AttachPointList = std::vector; inline std::string probe_orig_name(AttachPointList &aps) { std::vector ap_names; std::ranges::transform(aps, std::back_inserter(ap_names), [](const AttachPoint *ap) { return ap->raw_input; }); return util::str_join(ap_names, ","); } class Probe : public Node { public: explicit Probe(ASTContext &ctx, AttachPointList &&attach_points, Predicate *pred, Block *block, Location &&loc) : Node(ctx, std::move(loc)), attach_points(std::move(attach_points)), pred(pred), block(block), orig_name(probe_orig_name(this->attach_points)) {}; explicit Probe(ASTContext &ctx, const Probe &other, const Location &loc) : Node(ctx, loc + other.loc), attach_points(clone(ctx, other.attach_points, loc)), pred(clone(ctx, other.pred, loc)), block(clone(ctx, other.block, loc)), orig_name(other.orig_name), index_(other.index_) {}; AttachPointList attach_points; Predicate *pred = nullptr; Block *block = nullptr; std::string orig_name; std::string args_typename() const; 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: explicit SubprogArg(ASTContext &ctx, std::string name, SizedType type, Location &&loc) : Node(ctx, std::move(loc)), name(std::move(name)), type(std::move(type)) {}; explicit SubprogArg(ASTContext &ctx, const SubprogArg &other, const Location &loc) : Node(ctx, loc + other.loc), name(other.name), type(other.type) {}; const std::string name; SizedType type; }; using SubprogArgList = std::vector; class Subprog : public Node { public: explicit Subprog(ASTContext &ctx, std::string name, SizedType return_type, SubprogArgList &&args, StatementList &&stmts, Location &&loc) : Node(ctx, std::move(loc)), name(std::move(name)), return_type(std::move(return_type)), args(std::move(args)), stmts(std::move(stmts)) {}; explicit Subprog(ASTContext &ctx, const Subprog &other, const Location &loc) : Node(ctx, loc + other.loc), name(other.name), return_type(other.return_type), args(clone(ctx, other.args, loc)), stmts(clone(ctx, other.stmts, loc)) {}; const std::string name; SizedType return_type; SubprogArgList args; StatementList stmts; }; using SubprogList = std::vector; class Import : public Node { public: explicit Import(ASTContext &ctx, std::string name, Location &&loc) : Node(ctx, std::move(loc)), name(std::move(name)) {}; explicit Import(ASTContext &ctx, const Import &other, const Location &loc) : Node(ctx, loc + other.loc), name(other.name) {}; const std::string name; }; using ImportList = std::vector; class Macro : public Node { public: Macro(ASTContext &ctx, std::string name, ExpressionList &&vargs, BlockExpr *block_expr, Location &&loc) : Node(ctx, std::move(loc)), name(std::move(name)), vargs(std::move(vargs)), block(block_expr) {}; Macro(ASTContext &ctx, std::string name, ExpressionList &&vargs, Block *block, Location &&loc) : Node(ctx, std::move(loc)), name(std::move(name)), vargs(std::move(vargs)), block(block) {}; explicit Macro(ASTContext &ctx, const Macro &other, const Location &loc) : Node(ctx, loc + other.loc), name(other.name), vargs(clone(ctx, other.vargs, loc)), block(clone(ctx, other.block, loc)) {}; std::string name; ExpressionList vargs; std::variant block; }; using MacroList = std::vector; class Program : public Node { public: explicit Program(ASTContext &ctx, std::string c_definitions, Config *config, ImportList &&imports, RootStatements &&root_statements, Location &&loc) : Node(ctx, std::move(loc)), c_definitions(std::move(c_definitions)), config(config), imports(std::move(imports)) { for (auto &stmt : root_statements) { if (auto *map_decl = stmt.as()) { map_decls.push_back(map_decl); } else if (auto *macro = stmt.as()) { macros.push_back(macro); } else if (auto *function = stmt.as()) { functions.push_back(function); } else if (auto *probe = stmt.as()) { probes.push_back(probe); } } }; explicit Program(ASTContext &ctx, const Program &other, const Location &loc) : Node(ctx, loc + other.loc), c_definitions(other.c_definitions), config(clone(ctx, other.config, loc)), imports(clone(ctx, other.imports, loc)), map_decls(clone(ctx, other.map_decls, loc)), macros(clone(ctx, other.macros, loc)), functions(clone(ctx, other.functions, loc)), probes(clone(ctx, other.probes, loc)) {}; std::string c_definitions; Config *config = nullptr; ImportList imports; MapDeclList map_decls; MacroList macros; SubprogList functions; ProbeList probes; void clear_empty_probes(); }; std::string opstr(const Binop &binop); std::string opstr(const Unop &unop); std::string opstr(const Jump &jump); bool is_comparison_op(Operator op); SizedType ident_to_record(const std::string &ident, int pointer_level = 0); SizedType ident_to_sized_type(const std::string &ident); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/async_event_types.cpp000066400000000000000000000054071506776124200212300ustar00rootroot00000000000000#include #include #include "ast/async_event_types.h" #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 RuntimeError::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 }; } std::vector Join::asLLVMType(ast::IRBuilderBPF& b, uint32_t length) { return { b.getInt64Ty(), // action_id b.getInt64Ty(), // join_id llvm::ArrayType::get(b.getInt8Ty(), length), // join content }; } } // namespace bpftrace::AsyncEvent bpftrace-0.24.1/src/ast/async_event_types.h000066400000000000000000000055341506776124200206760ustar00rootroot00000000000000#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::ast { class IRBuilderBPF; } // namespace bpftrace::ast // 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::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; std::vector asLLVMType(ast::IRBuilderBPF& b, uint32_t length); } __attribute__((packed)); struct RuntimeError { 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)); struct Join { uint64_t action_id; uint64_t join_id; char content[0]; std::vector asLLVMType(ast::IRBuilderBPF& b, uint32_t length); } __attribute__((packed)); } // namespace bpftrace::AsyncEvent bpftrace-0.24.1/src/ast/async_ids.h000066400000000000000000000047511506776124200171100ustar00rootroot00000000000000#pragma once #include namespace bpftrace::ast { // Add new ids here #define FOR_LIST_OF_ASYNC_IDS(DO) \ DO(cat) \ DO(cgroup_path) \ DO(runtime_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 bpftrace::ast bpftrace-0.24.1/src/ast/attachpoint_parser.cpp000066400000000000000000000516461506776124200213660ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "ast/ast.h" #include "ast/attachpoint_parser.h" #include "ast/context.h" #include "ast/helpers.h" #include "types.h" #include "util/int_parser.h" #include "util/paths.h" #include "util/strings.h" #include "util/system.h" #include "util/wildcard.h" 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; } AttachPointParser::AttachPointParser(ASTContext &ctx, BPFtrace &bpftrace, bool listing) : ctx_(ctx), bpftrace_(bpftrace), 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; ap.addError() << 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 it = std::ranges::remove_if(probe->attach_points, [](const AttachPoint *ap) { return ap->provider.empty(); }); probe->attach_points.erase(it.begin(), it.end()); if (probe->attach_points.empty()) { probe->addError() << "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 (util::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().has_value() || (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 (util::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) util::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, Location(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::benchmark: return benchmark_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] == '$') { size_t i = idx + 1; size_t len = 0; while (i < raw.size() && std::isdigit(raw[i])) { if (len == 0 && raw[i] == '0') { break; } len++; i++; } std::string param_idx_str; if (len == 0 && (idx + 1) < raw.size()) { param_idx_str = raw.substr(idx + 1, 1); errs_ << "invalid trailing character for positional param: " << param_idx_str << ". Try quoting this entire part if this is intentional e.g. \"$" << param_idx_str << "\"."; return State::INVALID; } param_idx_str = raw.substr(idx + 1, len); auto param_idx = util::to_uint(param_idx_str, 10); if (!param_idx) { errs_ << "positional parameter is not valid: " << param_idx.takeError() << 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) + 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::benchmark_parser() { // Can only have reached here if provider is `bench` assert(ap_->provider == "bench"); if (ap_->provider == "bench") { if (parts_.size() != 2) { return argument_count_error(1); } ap_->target = parts_[1]; } 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 = util::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 = util::to_uint(offset_parts[1]); if (!res) { errs_ << "Invalid offset: " << res.takeError() << std::endl; return INVALID; } ap_->func_offset = *res; } // Default case (eg kprobe:func) else { ap_->func = parts_[func_idx]; } return OK; } AttachPointParser::State AttachPointParser::kretprobe_parser() { return kprobe_parser(false); } AttachPointParser::State AttachPointParser::uprobe_parser(bool allow_offset, bool allow_abs_addr) { const auto pid = bpftrace_.pid(); if (pid.has_value() && (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 = util::get_pid_exe(*pid); parts_[1] = target ? util::path_for_pid_mountns(*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 (!util::has_wildcard(parts_[1]) && parts_[1].starts_with("lib")) { // 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().value_or(0)); 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 = util::split_string(func, '+', true); if (offset_parts.size() != 2) { errs_ << "Invalid offset" << std::endl; return INVALID; } ap_->func = offset_parts[0]; auto res = util::to_uint(offset_parts[1]); if (!res) { errs_ << "Invalid offset: " << res.takeError() << std::endl; return INVALID; } ap_->func_offset = *res; } // Default case (eg uprobe:[addr][func]) else { if (allow_abs_addr) { auto res = util::to_uint(func); if (res) { if (util::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; } return OK; } AttachPointParser::State AttachPointParser::uretprobe_parser() { return uprobe_parser(false); } AttachPointParser::State AttachPointParser::usdt_parser() { if (bpftrace_.pid().has_value()) { // 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]; } 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 && util::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]; return OK; } // Used for both profile and interval probes AttachPointParser::State AttachPointParser::frequency_parser() { if (parts_.size() == 2) { if (util::has_wildcard(parts_[1])) { // Wildcards are allowed for listing ap_->target = parts_[1]; ap_->freq = 0; return OK; } auto res = util::to_uint(parts_[1]); if (!res) { errs_ << "Invalid rate of " << ap_->provider << " probe: " << res.takeError() << std::endl; return INVALID; } if (*res < 1000) { errs_ << "Invalid rate of " << ap_->provider << " probe. Minimum is 1000 or 1us. Found: " << *res << " nanoseconds" << std::endl; return INVALID; } ap_->target = "us"; // res is in nanoseconds ap_->freq = (*res / 1000); return OK; } if (parts_.size() != 3) { return argument_count_error(1, 2); } ap_->target = parts_[1]; auto res = util::to_uint(parts_[2]); if (!res) { errs_ << "Invalid rate of " << ap_->provider << " probe: " << res.takeError() << std::endl; return INVALID; } ap_->freq = *res; return OK; } AttachPointParser::State AttachPointParser::profile_parser() { return frequency_parser(); } AttachPointParser::State AttachPointParser::interval_parser() { return frequency_parser(); } 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 = util::to_uint(parts_[2]); if (!res) { errs_ << "Invalid count for " << ap_->provider << " probe: " << res.takeError() << std::endl; 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 = util::to_uint(parts_[2]); if (!res) { errs_ << "Invalid count for " << ap_->provider << " probe: " << res.takeError() << std::endl; 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 = util::to_uint(parts_[1]); if (!parsed) { errs_ << "Invalid function/address argument: " << parsed.takeError() << std::endl; return INVALID; } ap_->address = *parsed; } else { auto func_arg_parts = util::split_string(parts_[1], '+', true); if (func_arg_parts.size() != 2) { errs_ << "Invalid function/address argument: " << parts_[1] << std::endl; return INVALID; } ap_->func = func_arg_parts[0]; if (func_arg_parts[1].size() <= 3 || !func_arg_parts[1].starts_with("arg")) { errs_ << "Invalid function/address argument: " << func_arg_parts[1] << std::endl; return INVALID; } auto parsed = util::to_uint(func_arg_parts[1].substr(3)); if (!parsed) { errs_ << "Invalid function argument: " << parsed.takeError() << std::endl; return INVALID; } ap_->address = *parsed; } auto len_parsed = util::to_uint(parts_[2]); if (!len_parsed) { errs_ << "Invalid length argument: " << len_parsed.takeError() << 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 // fentry:bpf:[:prog_id]:prog_name if (parts_.size() != 2 && parts_.size() != 3 && parts_.size() != 4) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(1, 3); } if (parts_[1] == "bpf") { ap_->target = parts_[1]; if (parts_.size() == 2) { errs_ << "the 'bpf' variant of this probe requires a bpf program name " "and optional bpf program id"; return INVALID; } else if (parts_.size() == 3) { ap_->func = parts_[2]; } else { ap_->func = parts_[3]; if (parts_[2] != "*") { auto uint_res = util::to_uint(parts_[2]); if (!uint_res) { errs_ << "bpf program id must be a number or '*'"; return INVALID; } ap_->bpf_prog_id = *uint_res; } } return OK; } if (parts_.size() == 4) { errs_ << "Only the 'bpf' variant of this probe supports 4 arguments"; return INVALID; } if (parts_.size() == 3) { ap_->target = parts_[1]; ap_->func = parts_[2]; } else { ap_->func = parts_[1]; if (!util::has_wildcard(ap_->func)) { 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 = "*"; } 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; } ap_->func = parts_[1]; if (parts_.size() == 3) ap_->pin = parts_[2]; return OK; } AttachPointParser::State AttachPointParser::raw_tracepoint_parser() { if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(2, 1); } if (parts_.size() == 3) { ap_->target = parts_[1]; ap_->func = parts_[2]; } else { // This is to maintain backwards compatibility with older scripts // that couldn't include a target for a raw tracepoint. ap_->target = "*"; ap_->func = parts_[1]; } return OK; } // Note: listing changes the parsing semantics for attach points Pass CreateParseAttachpointsPass(bool listing) { return Pass::create("attachpoints", [listing](ASTContext &ast, BPFtrace &b) { AttachPointParser ap_parser(ast, b, listing); ap_parser.parse(); }); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/attachpoint_parser.h000066400000000000000000000041071506776124200210210ustar00rootroot00000000000000#pragma once #include #include #include #include "ast/ast.h" #include "ast/pass_manager.h" #include "bpftrace.h" namespace bpftrace::ast { class AttachPointParser { public: AttachPointParser(ASTContext &ctx, BPFtrace &bpftrace, 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 benchmark_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 frequency_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_; AttachPoint *ap_{ nullptr }; // Non-owning pointer std::stringstream errs_; std::vector parts_; AttachPointList new_attach_points; bool listing_; }; // The attachpoints are expanded in their own separate pass. Pass CreateParseAttachpointsPass(bool listing = false); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/clone.h000066400000000000000000000043061506776124200162300ustar00rootroot00000000000000#pragma once #include #include "ast/ast.h" #include "ast/context.h" namespace bpftrace::ast { class Expression; class Statement; class Iterable; class RootStatement; template struct Cloner { T operator()([[maybe_unused]] ASTContext &ctx, const T &v, [[maybe_unused]] const Location &loc) { return v; } }; template requires(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) struct Cloner { T operator()(ASTContext &ctx, const T &v, const Location &loc) { return std::visit([&](const auto &v) -> T { return clone(ctx, v, loc); }, v.value); } }; template struct Cloner> { std::vector operator()(ASTContext &ctx, const std::vector &obj, const Location &loc) { std::vector ret; for (const auto &v : obj) { ret.emplace_back(clone(ctx, v, loc)); } return ret; } }; template struct Cloner> { std::variant operator()(ASTContext &ctx, const std::variant &obj, const Location &loc) { return std::visit([&](const auto &v) -> std::variant { return clone(ctx, v, loc); }, obj); } }; template struct Cloner> { std::optional operator()(ASTContext &ctx, const std::optional &obj, const Location &loc) { if (!obj.has_value()) { return std::nullopt; } return clone(ctx, obj.value(), std::move(loc)); } }; template requires std::derived_from struct Cloner { T *operator()(ASTContext &ctx, const T *obj, const Location &loc) { return ctx.clone_node(obj, loc); } }; template T clone(ASTContext &ctx, const T &t, const Location &loc = Location()) { using V = std::remove_const_t>>; return Cloner()(ctx, t, loc); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/codegen_helper.cpp000066400000000000000000000026421506776124200204270ustar00rootroot00000000000000#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.value_type); } else if (map.value_type.IsRecordTy() || map.value_type.IsArrayTy()) { return !expr_type.is_internal; } return true; } bool needMapKeyAllocation(const Map &map, const Expression &key_expr) { if (inBpfMemory(key_expr.type())) { return !key_expr.type().IsSameSizeRecursive(map.key_type); } return true; } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/codegen_helper.h000066400000000000000000000022621506776124200200720ustar00rootroot00000000000000#pragma once #include "ast/ast.h" namespace bpftrace::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, const Expression &key_expr); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/context.cpp000066400000000000000000000017521506776124200171510ustar00rootroot00000000000000#include "ast/context.h" #include "ast/ast.h" #include "ast/diagnostic.h" namespace bpftrace::ast { ASTSource::ASTSource(std::string &&filename, std::string &&input) : filename(std::move(filename)), contents(std::move(input)) { std::stringstream ss(contents); std::string line; while (std::getline(ss, line)) { lines_.emplace_back(std::move(line)); } } ASTContext::ASTContext(std::string &&filename, std::string &&contents) : state_(std::make_unique()), source_( std::make_shared(std::move(filename), std::move(contents))) { } ASTContext::ASTContext(const std::string &filename, const std::string &contents) : ASTContext(std::string(filename), std::string(contents)) { } ASTContext::ASTContext() : ASTContext("", "") { } void ASTContext::clear() { root = nullptr; state_->nodes_.clear(); state_->diagnostics_->clear(); } ASTContext::State::State() : diagnostics_(std::make_unique()) { } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/context.h000066400000000000000000000064261506776124200166210ustar00rootroot00000000000000#pragma once #include #include #include "ast/diagnostic.h" #include "ast/pass_manager.h" namespace bpftrace { class Driver; namespace ast { class SourceLocation; class Node; class Program; template concept NodeType = std::derived_from; // Captures the original filename and source for a given AST. // // This is a heavy object, containing the full contents of the file. Only a // single instance of this class should be created and referenced. class ASTSource { public: ASTSource(std::string &&filename, std::string &&input); ASTSource(const ASTSource &other) = delete; ASTSource &operator=(const ASTSource &other) = delete; const std::string filename; const std::string contents; private: std::vector lines_; friend class SourceLocation; }; // Manages the lifetime of AST nodes. // // Nodes allocated by an ASTContext will be kept alive for the duration of the // owning ASTContext object. The ASTContext also owns the canonical instance of // the ASTSource, which is used by the Diagnostics to contextualize errors. class ASTContext : public ast::State<"ast"> { public: ASTContext(std::string &&filename, std::string &&contents); ASTContext(const std::string &filename, const std::string &contents); ASTContext(); // Creates and returns a pointer to an AST node. template constexpr T *make_node(Args &&...args) { auto uniq_ptr = std::make_unique(*this, wrap(std::forward(args))...); auto *raw_ptr = uniq_ptr.get(); state_->nodes_.push_back(std::move(uniq_ptr)); return raw_ptr; } template constexpr T *clone_node(const T *other, const Location &loc) { if (other == nullptr) { return nullptr; } auto uniq_ptr = std::make_unique(*this, *other, loc); auto *raw_ptr = uniq_ptr.get(); state_->nodes_.push_back(std::move(uniq_ptr)); return raw_ptr; } unsigned int node_count() { return state_->nodes_.size(); } Diagnostics &diagnostics() const { return *state_->diagnostics_; } std::shared_ptr source() const { return source_; } // clears all the nodes and diagnostics, but does not affect the underlying // `ASTSource` object. This is useful if you want to e.g. reparse the full // syntax tree in place. void clear(); // Root points to a node in `state_.nodes_`. Program *root = nullptr; private: // State owns the underlying nodes; they are permitted to take a reference to // this object since their lifetimes are bound. class State { public: State(); std::vector> nodes_; std::unique_ptr diagnostics_; }; // wrap potentially converts external types to internal ones. At the moment, // this automatically converts the parser `location` to the `Location` class // bound to the current source file. template auto wrap(T &&t) -> decltype(t) { return std::forward(t); } Location wrap(location loc) { return std::make_shared(SourceLocation(loc, source_)); }; std::unique_ptr state_; std::shared_ptr source_; friend class bpftrace::Driver; friend class Node; }; } // namespace ast } // namespace bpftrace bpftrace-0.24.1/src/ast/diagnostic.cpp000066400000000000000000000047731506776124200176170ustar00rootroot00000000000000#include #include #include "diagnostic.h" #include "log.h" namespace bpftrace::ast { std::stringstream& Diagnostic::addContext(Location loc) { loc_->contexts.emplace_back(std::make_shared(loc->current)); return loc_->contexts.back().msg; } void Diagnostics::emit(std::ostream& out) const { // Emit all errors first, following by all warnings. emit(out, Severity::Error); emit(out, Severity::Warning); } void Diagnostics::emit(std::ostream& out, Severity s) const { foreach(s, [this, s, &out](const Diagnostic& d) { emit(out, s, d); }); } void Diagnostics::emit(std::ostream& out, Severity s, const Diagnostic& d) const { // Build our sets of messages. std::vector> msgs; std::vector> parent_msgs; auto loc = d.loc(); if (loc) { msgs.emplace_back(d.msg(), loc->current); for (const auto& context : loc->contexts) { msgs.emplace_back(context.msg.str(), context.loc->current); } while (loc) { auto& parent = loc->parent; if (parent) { parent_msgs.emplace_back(parent->msg.str(), parent->loc->current); loc = parent->loc; } else { break; } } } switch (s) { case Severity::Warning: if (msgs.empty()) { LOG(WARNING, out) << d.msg(); } for (const auto& [msg, loc] : msgs) { LOG(WARNING, loc.source_location(), loc.source_context(), out) << msg; } for (const auto& msg : d.hints()) { LOG(HINT, out) << msg; } for (const auto& [msg, loc] : parent_msgs) { LOG(WARNING, loc.source_location(), loc.source_context(), out) << msg; } break; case Severity::Error: if (msgs.empty()) { LOG(ERROR, out) << d.msg(); } for (const auto& [msg, loc] : msgs) { LOG(ERROR, loc.source_location(), loc.source_context(), out) << msg; } for (const auto& msg : d.hints()) { LOG(HINT, out) << msg; } for (const auto& [msg, loc] : parent_msgs) { LOG(ERROR, loc.source_location(), loc.source_context(), out) << msg; } break; } } void Diagnostics::add(Diagnostics&& other) { if (diagnostics_.size() < other.diagnostics_.size()) { diagnostics_.resize(other.diagnostics_.size()); } for (size_t i = 0; i < other.diagnostics_.size(); i++) { for (auto& d : other.diagnostics_[i]) { diagnostics_[i].emplace_back(std::move(d)); } } } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/diagnostic.h000066400000000000000000000077101506776124200172560ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "ast/location.h" namespace bpftrace::ast { class ASTSource; // Diagnostic reflects a single error at a single source location. This is a // simple wrapper around a string for that message, and the location class. class Diagnostic { public: enum class Severity { Warning, Error, }; Diagnostic(const Location loc) : loc_(loc) {}; std::string msg() const { return msg_.str(); } std::vector hints() const { std::vector msgs; for (const auto& hint : hints_) { msgs.emplace_back(hint.str()); } return msgs; } const Location& loc() const { return loc_; } // Each diagnostic can potentially have hints attached, which is how to // effectively resolve this issue. std::stringstream& addHint() { return hints_.emplace_back(); } // Add additional context for the error. std::stringstream& addContext(Location loc); template Diagnostic& operator<<(const T& t) { msg_ << t; return *this; } private: std::stringstream msg_; std::vector hints_; Location loc_; }; class Diagnostics { public: using Severity = Diagnostic::Severity; // Note that the state of the diagnostics is really only a vector, which // provides well-defined `move` semantics. After the move, the vector is // guaranteed to be empty, which matches the state required for an empty // Diagnostics object. Diagnostics() = default; Diagnostics(Diagnostics&& other) = default; Diagnostics(const Diagnostics& other) = delete; Diagnostics& operator=(const Diagnostics& other) = delete; Diagnostics& operator=(Diagnostics&& other) = delete; template Diagnostic& add(Severity severity, Args... args) { auto index = static_cast(severity); if (diagnostics_.size() <= index) { diagnostics_.resize(index + 1); } auto& diags = diagnostics_[index]; auto& p = diags.emplace_back(std::make_unique(args...)); return *p.get(); } template Diagnostic& addError(Args... args) { return add(Severity::Error, args...); } template Diagnostic& addWarning(Args... args) { return add(Severity::Warning, args...); } bool has(Severity severity) const { auto index = static_cast(severity); return diagnostics_.size() > index && !diagnostics_[index].empty(); } void clear() { for (auto& diag : diagnostics_) { diag.clear(); } } // ok is the recommended short-hand for `has(Severity::Error)`. bool ok() const { return !has(Severity::Error); } // emit implements a default formatter of all diagnostics to a given stream. // The use of `emit` should be generally discouraged, especially by tests, // who should use more structured checks and avoid matching against the // specific format here. void emit(std::ostream& out) const; void emit(std::ostream& out, Severity s) const; void emit(std::ostream& out, Severity s, const Diagnostic& d) const; // adds all diagnostics from some other set. void add(Diagnostics&& other); private: void foreach(Severity severity, std::function fn) const { auto index = static_cast(severity); if (diagnostics_.size() <= index) { return; } for (const auto& diag : diagnostics_[index]) { fn(*diag); } } // Two-dimensional vector with all diagnostics. The first level is indexed by // severity, and the second level is the set of diagnostics for that level. // // N.B. we store diagnostics as a pointer because the lifetime is returned // early above, so they must not be moving at any point. std::vector>> diagnostics_; friend class Node; }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/dibuilderbpf.cpp000066400000000000000000000430361506776124200201210ustar00rootroot00000000000000#include "dibuilderbpf.h" #include #include #include "libbpf/bpf.h" #include "log.h" #include "struct.h" #include "types.h" #include "util/bpf_names.h" namespace bpftrace::ast { DIBuilderBPF::DIBuilderBPF(Module &module) : DIBuilder(module) { file = createFile("bpftrace.bpf.o", "."); } DILocalScope *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 (const auto &arg : args.fields) types.push_back(GetType(arg.type, false)); DISubroutineType *ditype = createSubroutineType(getOrCreateTypeArray(types)); std::string sanitised_name = util::sanitise_bpf_program_name( func.getName().str()); DISubprogram::DISPFlags flags = DISubprogram::SPFlagZero; if (!is_declaration) flags |= DISubprogram::SPFlagDefinition; if (llvm::Function::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); return subprog; } DILocalScope *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())); return 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::CreateTSeriesStructType(const SizedType &stype) { assert(stype.IsTSeriesTy()); // The first field is the value, the second field is metadata associated with // the value, and the third field is the epoch, a number representing the // bucket's time interval. The interpretation of the value and metadata fields // depends on the time series's aggregation function: // // +-----+-------+----------+----------------------------------------------+ // | agg | value | metadata | explanation | // +-----+-------+----------+----------------------------------------------+ // | avg | total | count | avg = total / count | // | max | max | is_set | is_set = "value is set" | // | min | min | is_set | is_set = "value is set" | // | sum | sum | - | metadata not used | // | - | value | now | now is the timestamp when value was recorded | // +-----+-------+----------+----------------------------------------------+ 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, getInt64Ty()), createMemberType(file, "", file, 0, stype.GetSize() * 8, 0, stype.GetSize() * 16, DINode::FlagZero, getInt64Ty()) }; DICompositeType *result = createStructType(file, "", file, 0, (stype.GetSize() * 8) * 3, 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.starts_with(struct_prefix)) name = name.substr(struct_prefix.length()); else if (name.starts_with(union_prefix)) 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); else if (stype.IsTSeriesTy()) return CreateTSeriesStructType(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) { // BPF requires 4-byte keys for array maps. if (map_type == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY || map_type == libbpf::BPF_MAP_TYPE_ARRAY) { assert(key_type.IsIntTy()); return getInt32Ty(); } if (map_type == libbpf::BPF_MAP_TYPE_RINGBUF) { assert(key_type.IsNoneTy()); return getInt64Ty(); } // Some map types need an extra 8-byte key. if (value_type.IsHistTy() || value_type.IsLhistTy() || value_type.IsTSeriesTy()) { 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); } DILocation *DIBuilderBPF::createDebugLocation(llvm::LLVMContext &ctx, DILocalScope *scope, const ast::Location &loc) { return llvm::DILocation::get(ctx, loc->line(), loc->column(), scope); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/dibuilderbpf.h000066400000000000000000000051241506776124200175620ustar00rootroot00000000000000#pragma once #include #include #include "ast/location.h" #include "types.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace::ast { using namespace llvm; class DIBuilderBPF : public DIBuilder { public: DIBuilderBPF(Module &module); DILocalScope *createFunctionDebugInfo(llvm::Function &func, const SizedType &ret_type, const Struct &args, bool is_declaration = false); DILocalScope *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 *CreateTSeriesStructType(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); DILocation *createDebugLocation(llvm::LLVMContext &ctx, DILocalScope *scope, const ast::Location &loc); 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 bpftrace::ast bpftrace-0.24.1/src/ast/helpers.cpp000066400000000000000000000014631506776124200171260ustar00rootroot00000000000000#include #include "ast/helpers.h" #include "log.h" namespace bpftrace { static std::unordered_set UNSAFE_BUILTIN_FUNCS = { "system", "signal", "override", }; static std::unordered_set COMPILE_TIME_FUNCS = { "cgroupid" }; static std::unordered_set UPROBE_LANGS = { "cpp" }; bool is_unsafe_func(const std::string &func_name) { return UNSAFE_BUILTIN_FUNCS.contains(func_name); } bool is_compile_time_func(const std::string &func_name) { return COMPILE_TIME_FUNCS.contains(func_name); } bool is_supported_lang(const std::string &lang) { return UPROBE_LANGS.contains(lang); } bool is_type_name(std::string_view str) { return str.starts_with("struct ") || str.starts_with("union ") || str.starts_with("enum "); } } // namespace bpftrace bpftrace-0.24.1/src/ast/helpers.h000066400000000000000000000004261506776124200165710ustar00rootroot00000000000000#pragma once #include namespace bpftrace { 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); } // namespace bpftrace bpftrace-0.24.1/src/ast/irbuilderbpf.cpp000066400000000000000000003334761506776124200201510ustar00rootroot00000000000000#include #include #include #include "arch/arch.h" #include "ast/async_event_types.h" #include "ast/codegen_helper.h" #include "ast/irbuilderbpf.h" #include "async_action.h" #include "bpfmap.h" #include "bpftrace.h" #include "globalvars.h" #include "log.h" #include "types.h" #include "util/bpf_funcs.h" #include "util/exceptions.h" 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); } __builtin_unreachable(); } } // 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(const Location &loc, bool force_init) { const auto &pidns = bpftrace_.get_pidns_self_stat(); if (!force_init && pidns && 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( 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 otherwise Value *pidtgid = CreateGetPidTgid(loc); Value *pid = CreateTrunc(CreateLShr(pidtgid, 32), getInt32Ty(), "pid"); return pid; } Value *IRBuilderBPF::CreateGetTid(const Location &loc, bool force_init) { const auto &pidns = bpftrace_.get_pidns_self_stat(); if (!force_init && pidns && 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( 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 otherwise Value *pidtgid = CreateGetPidTgid(loc); Value *tid = CreateTrunc(pidtgid, getInt32Ty(), "tid"); return tid; } AllocaInst *IRBuilderBPF::CreateUSym(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(loc, false); 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( const 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(getContext(), 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(getContext(), 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. /// /// Only one type is not converted directly into an LLVM type: /// - structs (records) are represented as byte arrays llvm::Type *IRBuilderBPF::GetType(const SizedType &stype) { 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::string ty_name; for (const auto &elem : stype.GetFields()) { const auto &elemtype = elem.type; llvm_elems.emplace_back(GetType(elemtype)); ty_name += typestr(elemtype, true) + "_"; } ty_name += "_tuple_t"; ty = GetStructType(ty_name, llvm_elems, false); } else if (stype.IsStack()) { ty = GetStackStructType(stype.IsUstackTy()); } else if (stype.IsPtrTy()) { ty = getPtrTy(); } else if (stype.IsVoidTy()) { ty = getVoidTy(); } else if (stype.IsBoolTy()) { ty = getInt1Ty(); } 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 if (stype.IsTSeriesTy()) { std::vector llvm_elems = { getInt64Ty(), getInt64Ty(), getInt64Ty() }; ty = GetStructType("t_series_val", llvm_elems, false); } else { ty = GetType(stype); } return ty; } /// Creates a call to a BPF helper function /// /// A call to a helper function can be marked as "pure" to allow LLVM to /// optimise around it. /// /// ** BE VERY CAREFUL when marking a helper function as pure - marking an /// impure helper as pure can result in undefined behaviour. ** /// /// Guidelines for deciding if a helper can be considered pure: /// - It must always return the same value when called repeatedly with the same /// arguments within a single probe's context /// - It must not read or write any memory in the BPF address space (e.g. it /// mustn't take any pointers to BPF memory as parameters) /// - It must not have any intentional side effects outside of the BPF address /// space, otherwise these may be optimised out, e.g. pushing to the ring /// buffer, signalling a process CallInst *IRBuilderBPF::CreateHelperCall(libbpf::bpf_func_id func_id, FunctionType *helper_type, ArrayRef args, bool is_pure, const Twine &Name, const Location &loc) { bpftrace_.helper_use_loc_[func_id].emplace_back(RuntimeErrorId::HELPER_ERROR, func_id, loc); PointerType *helper_ptr_type = PointerType::get(getContext(), 0); Constant *helper_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(func_id), helper_ptr_type); CallInst *call = createCall(helper_type, helper_func, args, Name); // When we tell LLVM that this function call "does not access memory", this // only refers to BPF memory. Accessing kernel or user memory is fine within // a "pure helper", as we can consider kernel/user memory as constant. if (is_pure) call->setDoesNotAccessMemory(); return call; } 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(getContext(), 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(getContext(), 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, uint64_t pad) { const auto max_strlen = bpftrace_.config_->max_strlen + pad; const auto str_type = CreateArray(max_strlen, CreateInt8()); return createAllocation(bpftrace::globalvars::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::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::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::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::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::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::MAP_KEY_BUFFER, GetType(value_type), name, loc, [](AsyncIds &async_ids) { return async_ids.map_key(); }); } Value *IRBuilderBPF::createAllocation( std::string_view global_var_name, 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_->on_stack_limit; if (obj_size > on_stack_limit) { return createScratchBuffer(global_var_name, loc, gen_async_id_cb ? (*gen_async_id_cb)(async_ids_) : 0); } return CreateAllocaBPF(obj_type, name); } Value *IRBuilderBPF::createScratchBuffer(std::string_view global_var_name, const Location &loc, size_t key) { // These specific global variables are nested arrays // (see get_sized_type in globalvars.cpp). // The top level array is for each CPU where the length is // MAX_CPU_ID + 1. This is so there is no contention between CPUs // when accessing this global value. // The second level array is for each key where the length is // the number of elements for this specific global, e.g. if // there are multiple strings that can't fit on the BPF stack // then there will be one element per string. // The last level is either an array of bytes (e.g. for strings) // or a single value (e.g. for ints like the EVENT_LOSS_COUNTER) const auto global_name = std::string(global_var_name); bpftrace_.resources.global_vars.check_index(global_name, bpftrace_.resources, key); auto sized_type = bpftrace_.resources.global_vars.get_sized_type( global_name, bpftrace_.resources, *bpftrace_.config_); auto *cpu_id = CreateGetCpuId(loc); auto *max = CreateLoad(getInt64Ty(), module_.getGlobalVariable( std::string(bpftrace::globalvars::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] // More details on using GEP: https://llvm.org/docs/LangRef.html#id236 if (sized_type.GetElementTy()->GetElementTy()->IsArrayTy()) { return CreateGEP( GetType(sized_type), module_.getGlobalVariable(global_name), { getInt64(0), bounded_cpu_id, getInt64(key), getInt64(0) }); } return CreateGEP(GetType(sized_type), module_.getGlobalVariable(global_name), { getInt64(0), bounded_cpu_id, getInt64(key) }); } // 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(Map &map, Value *key, const Location &loc) { return CreateMapLookupElem(map.ident, key, map.value_type, loc); } Value *IRBuilderBPF::CreateMapLookupElem(const std::string &map_name, Value *key, SizedType &type, const Location &loc) { 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 { CreateStore(CreateLoad(getPtrTy(), call), value); } CreateBr(lookup_merge_block); SetInsertPoint(lookup_failure_block); if (needMemcpy(type)) CreateMemsetBPF(value, getInt8(0), type.GetSize()); else CreateStore(Constant::getNullValue(GetType(type)), value); CreateRuntimeError(RuntimeErrorId::HELPER_ERROR, 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(GetType(type), value); if (dyn_cast(value)) CreateLifetimeEnd(value); return ret; } Value *IRBuilderBPF::CreatePerCpuMapAggElems(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; 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(std::string( bpftrace::globalvars::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); CreateRuntimeError(RuntimeErrorId::HELPER_ERROR, 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(const std::string &map_ident, Value *key, Value *val, const Location &loc, int64_t flags) { Value *map_ptr = GetMapVar(map_ident); 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(getContext(), 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(call, libbpf::BPF_FUNC_map_update_elem, loc); } CallInst *IRBuilderBPF::CreateMapDeleteElem(Map &map, Value *key, bool ret_val_discarded, const Location &loc) { 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(getContext(), 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( call, libbpf::BPF_FUNC_map_delete_elem, loc, !ret_val_discarded); return call; } Value *IRBuilderBPF::CreateForRange(Value *iters, Value *callback, Value *callback_ctx, const Location &loc) { // long bpf_loop(__u32 nr_loops, void *callback_fn, void *callback_ctx, u64 // flags) // // Return: 0 on success or negative error // // callback is long (*callback_fn)(u64 index, void *ctx); iters = CreateIntCast(iters, getInt32Ty(), true); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *is_positive_block = BasicBlock::Create(module_.getContext(), "is_positive", parent); BasicBlock *merge_block = BasicBlock::Create(module_.getContext(), "merge", parent); Value *is_positive = CreateICmpSGT(iters, getInt32(0), "is_positive_cond"); CreateCondBr(is_positive, is_positive_block, merge_block); SetInsertPoint(is_positive_block); FunctionType *bpf_loop_type = FunctionType::get( getInt64Ty(), { getInt32Ty(), callback->getType(), getPtrTy(), getInt64Ty() }, false); PointerType *bpf_loop_ptr_type = PointerType::get(getContext(), 0); Constant *bpf_loop_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64( libbpf::BPF_FUNC_loop), bpf_loop_ptr_type); CallInst *call = createCall( bpf_loop_type, bpf_loop_func, { iters, callback, callback_ctx ? CreateIntToPtr(callback_ctx, getPtrTy()) : GetNull(), /*flags=*/getInt64(0) }, "bpf_loop"); CreateHelperErrorCond(call, libbpf::BPF_FUNC_loop, loc); CreateBr(merge_block); SetInsertPoint(merge_block); return call; } Value *IRBuilderBPF::CreateForEachMapElem(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(getContext(), 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(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), GetNull(), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); CreateLifetimeEnd(key); Value *prev_value = CREATE_ATOMIC_RMW(AtomicRMWInst::BinOp::Xchg, call, 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". CreateIncEventLossCounter(loc); 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), GetNull(), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); CreateLifetimeEnd(key); CreateStore(getInt64(0), call); 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 *dst, llvm::Value *size, Value *src, AddrSpace as, const Location &loc) { 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(getContext(), 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(call, read_fn, loc); } CallInst *IRBuilderBPF::CreateProbeReadStr(Value *dst, size_t size, Value *src, AddrSpace as, const Location &loc) { return CreateProbeReadStr(dst, getInt32(size), src, as, loc); } CallInst *IRBuilderBPF::CreateProbeReadStr(Value *dst, llvm::Value *size, Value *src, AddrSpace as, const Location &loc) { 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(getContext(), 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(call, read_fn, loc); return call; } Value *IRBuilderBPF::CreateUSDTReadArgument(Value *ctx, struct bcc_usdt_argument *argument, Builtin &builtin, AddrSpace as, const Location &loc) { // 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) builtin.addError() << "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?? builtin.addError() << "index register set without base register;" << " this case is not yet handled"; } Value *result = nullptr; if (argument->valid & BCC_USDT_ARGUMENT_BASE_REGISTER_NAME) { auto offset = arch::Host::register_to_pt_regs_offset( argument->base_register_name); if (!offset) { builtin.addError() << "offset for register " << argument->base_register_name << " not known"; return getInt32(0); } // 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.value()), "load_register"); AllocaInst *dst = CreateAllocaBPF(builtin.builtin_type, builtin.ident); Value *index_offset = nullptr; if (argument->valid & BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME) { auto ioffset = arch::Host::register_to_pt_regs_offset( argument->index_register_name); if (!ioffset) { builtin.addError() << "offset for register " << argument->index_register_name << " not known"; return getInt32(0); } index_offset = CreateSafeGEP( getInt8Ty(), ctx, getInt64(ioffset.value()), "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(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, std::optional pid, AddrSpace as, const Location &loc) { struct bcc_usdt_argument argument; void *usdt; if (pid.has_value()) { if (!attach_point->target.empty()) { auto real_path = std::filesystem::absolute(attach_point->target).string(); usdt = bcc_usdt_new_frompid(*pid, real_path.c_str()); } else { usdt = bcc_usdt_new_frompid(*pid, nullptr); }; } else { usdt = bcc_usdt_new_frompath(attach_point->target.c_str()); } if (usdt == nullptr) { throw util::FatalUserException( "failed to initialize usdt context for probe " + attach_point->target); } 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) { throw util::FatalUserException("couldn't get argument " + std::to_string(arg_num) + " for " + attach_point->target + ":" + attach_point->ns + ":" + attach_point->func); } 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 strings. It returns 0 // if all are equal and 1 if any are different. // // strcmp(String val1, String val2) // { // for (size_t i = 0; i < n; i++) // { // // if (val1[i] != val2[i]) // { // return 1; // } // if (val1[i] == NULL) // { // break; // } // } // // return 0; // } llvm::Function *parent = GetInsertBlock()->getParent(); AllocaInst *store = CreateAllocaBPF(getInt64Ty(), "strcmp.result"); BasicBlock *str_ne = BasicBlock::Create(module_.getContext(), "strcmp.false", parent); BasicBlock *done = BasicBlock::Create(module_.getContext(), "strcmp.done", parent); CreateStore(getInt64(inverse ? 0 : 1), 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(getInt64(inverse ? 1 : 0), store); CreateBr(str_ne); SetInsertPoint(str_ne); Value *result = CreateLoad(getInt64Ty(), store); CreateLifetimeEnd(store); 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, {}, false, "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, {}, false, "jiffies64", loc); } Value *IRBuilderBPF::CreateIntegerArrayCmp(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(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(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); auto *res = CreateHelperCall(libbpf::BPF_FUNC_get_current_pid_tgid, getpidtgid_func_type, {}, true, "get_pid_tgid", loc); return res; } void IRBuilderBPF::CreateGetNsPidTgid(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 const 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) }, false, "get_ns_pid_tgid", loc); CreateHelperErrorCond(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, {}, true, "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, {}, true, "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, {}, true, "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, {}, true, "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, {}, true, "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, {}, false, "get_random", loc); } CallInst *IRBuilderBPF::CreateGetStack(Value *ctx, bool ustack, Value *buf, StackType stack_type, const Location &loc) { 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 }, false, "get_stack", loc); CreateHelperErrorCond(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 }, true, "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 }, true, "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 }, true, "this_cpu_ptr", loc); } void IRBuilderBPF::CreateGetCurrentComm(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) }, false, "get_comm", loc); CreateHelperErrorCond(call, libbpf::BPF_FUNC_get_current_comm, loc); } CallInst *IRBuilderBPF::CreateGetSocketCookie(Value *var, const Location &loc) { // u64 bpf_get_socket_cookie(struct sock *sk) // Return: // A 8-byte long unique number or 0 if *sk* is NULL. FunctionType *get_socket_cookie_func_type = FunctionType::get( getInt64Ty(), { var->getType() }, false); return CreateHelperCall(libbpf::BPF_FUNC_get_socket_cookie, get_socket_cookie_func_type, { var }, true, "get_socket_cookie", loc); } void IRBuilderBPF::CreateOutput(Value *data, size_t size, const Location &loc) { assert(data && data->getType()->isPointerTy()); CreateRingbufOutput(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) }, false, "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); CreateIncEventLossCounter(loc); CreateBr(merge_block); SetInsertPoint(merge_block); } void IRBuilderBPF::CreateIncEventLossCounter(const Location &loc) { auto *value = createScratchBuffer(bpftrace::globalvars::EVENT_LOSS_COUNTER, loc, 0); CreateStore(CreateAdd(CreateLoad(getInt64Ty(), value), getInt64(1)), value); } void IRBuilderBPF::CreatePerCpuMapElemInit(Map &map, Value *key, Value *val, const Location &loc) { AllocaInst *initValue = CreateAllocaBPF(val->getType(), "initial_value"); CreateStore(val, initValue); CreateMapUpdateElem(map.ident, key, initValue, loc, BPF_ANY); CreateLifetimeEnd(initValue); } void IRBuilderBPF::CreatePerCpuMapElemAdd(Map &map, Value *key, Value *val, const Location &loc) { CallInst *call = CreateMapLookup(map, 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); AllocaInst *value = CreateAllocaBPF(map.value_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); CreatePerCpuMapElemInit(map, key, val, loc); CreateBr(lookup_merge_block); SetInsertPoint(lookup_merge_block); CreateLifetimeEnd(value); } 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, false, "trace_printk", loc); } void IRBuilderBPF::CreateSignal(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(getContext(), 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(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(getContext(), 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(getContext(), 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)), true, /*volatile*/ name); 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) { std::optional reg; if (builtin == "__builtin_retval") { reg = arch::Host::return_value(); } else if (builtin == "__builtin_func") { reg = arch::Host::pc_value(); } else if (builtin.starts_with("arg")) { size_t n = static_cast(atoi(builtin.substr(3).c_str())); const auto &arguments = arch::Host::arguments(); if (n < arguments.size()) { reg = arguments[n]; } } if (!reg.has_value()) { LOG(BUG) << "unknown builtin: " << builtin; __builtin_unreachable(); } auto offset = arch::Host::register_to_pt_regs_offset(reg.value()); if (!offset.has_value()) { LOG(BUG) << "invalid register `" << reg.value() << " for builtin: " << builtin; __builtin_unreachable(); } return CreateRegisterRead(ctx, offset.value(), builtin); } Value *IRBuilderBPF::CreateRegisterRead(Value *ctx, size_t 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. // // FIXME(#3873): Not clear if this applies as a general rule, best to allow // these to be resolved via field names and BTF directly in the future. llvm::Type *registerTy = getPointerStorageTy(); Value *result = CreateLoad(registerTy, CreateSafeGEP(getInt8Ty(), ctx, getInt64(offset)), true, /*volatile*/ name); 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::CreateRuntimeError(RuntimeErrorId rte_id, const Location &loc) { CreateRuntimeError( rte_id, getInt64(0), static_cast(-1), loc); } void IRBuilderBPF::CreateRuntimeError(RuntimeErrorId rte_id, Value *return_value, libbpf::bpf_func_id func_id, const Location &loc) { if (rte_id == RuntimeErrorId::HELPER_ERROR) { 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_.runtime_error(); bpftrace_.resources.runtime_error_info.try_emplace( error_id, RuntimeErrorInfo(rte_id, func_id, loc)); auto elements = AsyncEvent::RuntimeError().asLLVMType(*this); StructType *runtime_error_struct = GetStructType("runtime_error_t", elements, true); AllocaInst *buf = CreateAllocaBPF(runtime_error_struct, "runtime_error_t"); CreateStore( GetIntSameSize(static_cast( async_action::AsyncAction::runtime_error), elements.at(0)), CreateGEP(runtime_error_struct, buf, { getInt64(0), getInt32(0) })); CreateStore( GetIntSameSize(error_id, elements.at(1)), CreateGEP(runtime_error_struct, buf, { getInt64(0), getInt32(1) })); CreateStore( return_value, CreateGEP(runtime_error_struct, buf, { getInt64(0), getInt32(2) })); const auto &layout = module_.getDataLayout(); auto struct_size = layout.getTypeAllocSize(runtime_error_struct); CreateOutput(buf, struct_size, loc); CreateLifetimeEnd(buf); } void IRBuilderBPF::CreateHelperErrorCond(Value *return_value, libbpf::bpf_func_id func_id, const Location &loc, bool suppress_error) { if (bpftrace_.helper_check_level_ == 0 || suppress_error || (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 = CreateICmpSGE(ret, Constant::getNullValue(ret->getType())); CreateCondBr(condition, helper_merge_block, helper_failure_block); SetInsertPoint(helper_failure_block); CreateRuntimeError(RuntimeErrorId::HELPER_ERROR, ret, func_id, loc); CreateBr(helper_merge_block); SetInsertPoint(helper_merge_block); } void IRBuilderBPF::CreatePath(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 }, false, "d_path", loc); CreateHelperErrorCond(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(getContext(), 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(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 *dst, const SizedType &type, Value *src, const Location &loc, std::optional addrSpace) { AddrSpace as = addrSpace ? addrSpace.value() : type.GetAS(); if (!type.IsPtrTy()) { CreateProbeRead(dst, getInt32(type.GetSize()), src, as, loc); return; } // 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()->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(dst, getInt32(ptr_size), src, as, loc); } llvm::Value *IRBuilderBPF::CreateDatastructElemLoad(const SizedType &type, llvm::Value *ptr) { llvm::Type *ptr_storage_ty = getPointerStorageTy(); if (!type.IsPtrTy() || ptr_storage_ty == getInt64Ty()) return CreateLoad(GetType(type), ptr, true /*volatile*/); 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, true /*volatile*/); return CreateIntCast(expr, getInt64Ty(), false); } llvm::Value *IRBuilderBPF::CreatePtrOffset(const SizedType &type, llvm::Value *index) { size_t elem_size = type.IsPtrTy() ? getPointerStorageTy()->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() { static int ptr_width = arch::Host::kernel_ptr_width(); return getIntNTy(ptr_width); } void IRBuilderBPF::CreateMinMax(Value *val, Value *val_ptr, Value *is_set_ptr, bool max, bool is_signed) { llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *is_set_block = BasicBlock::Create(module_.getContext(), "is_set", parent); BasicBlock *min_max_block = BasicBlock::Create(module_.getContext(), "min_max", parent); BasicBlock *merge_block = BasicBlock::Create(module_.getContext(), "merge", parent); Value *curr = CreateLoad(getInt64Ty(), val_ptr); Value *is_set_condition = CreateICmpEQ(CreateLoad(getInt64Ty(), is_set_ptr), getInt64(1), "is_set_cond"); CreateCondBr(is_set_condition, is_set_block, min_max_block); SetInsertPoint(is_set_block); Value *min_max_condition; if (max) { min_max_condition = is_signed ? CreateICmpSGE(val, curr) : CreateICmpUGE(val, curr); } else { min_max_condition = is_signed ? CreateICmpSGE(curr, val) : CreateICmpUGE(curr, val); } CreateCondBr(min_max_condition, min_max_block, merge_block); SetInsertPoint(min_max_block); CreateStore(val, val_ptr); CreateStore(getInt64(1), is_set_ptr); CreateBr(merge_block); SetInsertPoint(merge_block); } llvm::Value *IRBuilderBPF::CreateCheckedBinop(Binop &binop, Value *lhs, Value *rhs) { assert(binop.op == Operator::DIV || binop.op == Operator::MOD); // We need to do an explicit 0 check or else a Clang compiler optimization // will assume that the value can't ever be 0, as this is undefined behavior, // and remove a conditional null check for map values which will return 0 if // the map value is null (this happens in CreateMapLookupElem). This would be // fine but the BPF verifier will complain about the lack of a null check. // Issue: https://github.com/bpftrace/bpftrace/issues/4379 // From Google's AI: "LLVM, like other optimizing compilers, is allowed to // make assumptions based on the absence of undefined behavior. If a program's // code, after optimization, would result in undefined behavior (like division // by zero by CreateURem), the compiler is free to make transformations that // assume such a situation will never occur." AllocaInst *op_result = CreateAllocaBPF(getInt64Ty(), "op_result"); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *is_zero = BasicBlock::Create(module_.getContext(), "is_zero", parent); BasicBlock *not_zero = BasicBlock::Create(module_.getContext(), "not_zero", parent); BasicBlock *zero_merge = BasicBlock::Create(module_.getContext(), "zero_merge", parent); Value *cond = CreateICmpEQ(rhs, getInt64(0), "zero_cond"); CreateCondBr(cond, is_zero, not_zero); SetInsertPoint(is_zero); CreateStore(getInt64(1), op_result); CreateRuntimeError(RuntimeErrorId::DIVIDE_BY_ZERO, binop.loc); CreateBr(zero_merge); SetInsertPoint(not_zero); if (binop.op == Operator::MOD) { CreateStore(CreateURem(lhs, rhs), op_result); } else if (binop.op == Operator::DIV) { CreateStore(CreateUDiv(lhs, rhs), op_result); } CreateBr(zero_merge); SetInsertPoint(zero_merge); auto *result = CreateLoad(getInt64Ty(), op_result); CreateLifetimeEnd(op_result); return result; } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/irbuilderbpf.h000066400000000000000000000372441506776124200176100ustar00rootroot00000000000000#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::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); 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(Map &map, Value *key, const Location &loc); Value *CreateMapLookupElem(const std::string &map_name, Value *key, SizedType &type, const Location &loc); Value *CreatePerCpuMapAggElems(Map &map, Value *key, const SizedType &type, const Location &loc); void CreateMapUpdateElem(const std::string &map_ident, Value *key, Value *val, const Location &loc, int64_t flags = 0); CallInst *CreateMapDeleteElem(Map &map, Value *key, bool ret_val_discarded, const Location &loc); Value *CreateForRange(Value *iters, Value *callback, Value *callback_ctx, const Location &loc); Value *CreateForEachMapElem(Map &map, Value *callback, Value *callback_ctx, const Location &loc); void CreateProbeRead(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 *dst, 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); CallInst *CreateProbeReadStr(Value *dst, llvm::Value *size, Value *src, AddrSpace as, const Location &loc); CallInst *CreateProbeReadStr(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_num, Builtin &builtin, std::optional pid, AddrSpace as, const Location &loc); Value *CreateStrncmp(Value *str1, Value *str2, uint64_t n, bool inverse); Value *CreateStrcontains(Value *haystack, uint64_t haystack_sz, Value *needle, uint64_t needle_sz); Value *CreateIntegerArrayCmp(Value *val1, Value *val2, const SizedType &val1_type, const SizedType &val2_type, 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 *CreateGetSocketCookie(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, uint64_t pad = 0); 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, bool is_pure, const Twine &Name, const Location &loc); CallInst *createCall(FunctionType *callee_type, Value *callee, ArrayRef args, const Twine &Name); void CreateGetCurrentComm(AllocaInst *buf, size_t size, const Location &loc); void CreateOutput(Value *data, size_t size, const Location &loc); void CreateIncEventLossCounter(const Location &loc); void CreatePerCpuMapElemInit(Map &map, Value *key, Value *val, const Location &loc); void CreatePerCpuMapElemAdd(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 *sig, const Location &loc); void CreateOverrideReturn(Value *ctx, Value *rc); void CreateRuntimeError(RuntimeErrorId rte_id, const Location &loc); void CreateRuntimeError(RuntimeErrorId rte_id, Value *return_value, libbpf::bpf_func_id func_id, const Location &loc); void CreateHelperErrorCond(Value *return_value, libbpf::bpf_func_id func_id, const Location &loc, bool suppress_error = false); StructType *GetStackStructType(bool is_ustack); StructType *GetStructType(const std::string &name, const std::vector &elements, bool packed = false); Value *CreateGetPid(const Location &loc, bool force_init); Value *CreateGetTid(const Location &loc, bool force_init); AllocaInst *CreateUSym(Value *val, int probe_id, const Location &loc); Value *CreateRegisterRead(Value *ctx, const std::string &builtin); Value *CreateRegisterRead(Value *ctx, size_t 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 *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); // 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 offsets, 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(); void CreateMinMax(Value *val, Value *val_ptr, Value *is_set_ptr, bool max, bool is_signed); llvm::Value *CreateCheckedBinop(Binop &binop, Value *lhs, Value *rhs); private: Module &module_; BPFtrace &bpftrace_; AsyncIds &async_ids_; CallInst *CreateGetPidTgid(const Location &loc); void CreateGetNsPidTgid(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(std::string_view global_var_name, 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(std::string_view global_var_name, const Location &loc, size_t key); libbpf::bpf_func_id selectProbeReadHelper(AddrSpace as, bool str); void CreateRingbufOutput(Value *data, size_t size, const Location &loc); 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 bpftrace::ast bpftrace-0.24.1/src/ast/location.cpp000066400000000000000000000046541506776124200173010ustar00rootroot00000000000000#include "location.h" #include #include "ast/context.h" namespace bpftrace::ast { SourceLocation::SourceLocation(location loc, std::shared_ptr source) : line_range_(loc.begin.line, loc.end.line), column_range_(loc.begin.column, loc.end.column), source_(std::move(source)) { } std::string SourceLocation::filename() const { if (source_) { return source_->filename; } return ""; } std::string SourceLocation::source_location() const { std::stringstream ss; if (source_) { ss << source_->filename << ":"; } if (line_range_.first != line_range_.second) { ss << line_range_.first << "-" << line_range_.second; return ss.str(); } ss << line_range_.first << ":"; ss << column_range_.first << "-" << column_range_.second; return ss.str(); } std::vector SourceLocation::source_context() const { std::vector result; // Is there source available? if (!source_ || line_range_.first == 0) { return result; } // Multi-lines just include all context. if (line_range_.first != line_range_.second) { assert(line_range_.first < line_range_.second); for (unsigned int i = line_range_.first; i <= line_range_.second; i++) { assert(i <= source_->lines_.size()); result.push_back(source_->lines_[i - 1]); } return result; } // Single line includes just the relevant context. if (line_range_.first > source_->lines_.size()) { return result; // Nothing available. } auto &srcline = source_->lines_[line_range_.first - 1]; std::stringstream orig; for (auto c : srcline) { if (c == '\t') orig << " "; else orig << c; } result.emplace_back(orig.str()); std::stringstream select; for (unsigned int x = 0; x < srcline.size() && x < column_range_.second - 1; x++) { char marker = x < column_range_.first - 1 ? ' ' : '~'; if (srcline[x] == '\t') { select << std::string(4, marker); } else { select << marker; } } result.emplace_back(select.str()); return result; } Location operator+(const Location &orig, const Location &expansion) { if (expansion == nullptr) { return orig; } if (orig == nullptr) { return expansion; } auto nlink = std::make_shared(expansion->current); nlink->parent.emplace(LocationChain::Context(Location(orig))); nlink->parent->msg << "expanded from"; return nlink; } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/location.h000066400000000000000000000063151506776124200167420ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include "location.hh" namespace bpftrace::ast { class ASTSource; // SourceLocation refers to a single location in a source file. // // It does not contain any additional context. class SourceLocation { public: SourceLocation() = default; SourceLocation(const SourceLocation &) = default; SourceLocation &operator=(const SourceLocation &) = default; SourceLocation(location loc, std::shared_ptr source = {}); // Canonical filename. std::string filename() const; // Produce a canonical string showing the filename, line range and/or column // range. This is of the form `::` but may also take the // form `:`. std::string source_location() const; // Produce a sequence of lines that highlights the specific source context. // Note that these should be emitted such that the columns for each line line // up when they are displayed. std::vector source_context() const; // Canonical line number, will be start of range. unsigned int line() const { return line_range_.first; }; // Canonical column number, will be start of range. unsigned int column() const { return column_range_.first; }; private: using range_t = std::pair; SourceLocation(range_t &&lines, range_t &&columns, std::shared_ptr source) : line_range_(std::move(lines)), column_range_(std::move(columns)), source_(std::move(source)) {}; range_t line_range_; range_t column_range_; std::shared_ptr source_; }; class LocationChain; using Location = std::shared_ptr; // LocationChain is what is used to represent a canonical location in the AST. // It is a effectively a linked list of source locations, where each link is // annotated by some additional amount of context. // // Note that each LocationChain should be wrapped as a `shared_ptr`, and the // `Location` alias should be the way that it is referred to broadly. class LocationChain { public: struct Context { Context(Location &&loc) : loc(std::move(loc)) {}; std::stringstream msg; const Location loc; }; LocationChain(const SourceLocation &loc) : current(loc) {}; // See above. std::string filename() const { return current.filename(); } std::string source_location() const { return current.source_location(); } std::vector source_context() const { return current.source_context(); } unsigned int line() const { return current.line(); } unsigned int column() const { return current.column(); } // This may be modified for a node, typically when copying. For example, when // copying a node you may automatically modify the location in order to // inject a parent that has "expanded from <...>". When these are displayed as // diagnostics, they are unpacked in reverse. std::optional parent; const SourceLocation current; std::vector contexts; }; Location operator+(const Location &orig, const Location &expansion); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/pass_manager.cpp000066400000000000000000000040731506776124200201240ustar00rootroot00000000000000#include "ast/pass_manager.h" #include "ast/context.h" #include "log.h" #include "util/result.h" namespace bpftrace::ast { std::atomic PassContext::next_type_id_; std::unordered_map PassContext::type_names_; bool PassContext::ok() const { int type_id = TypeId::type_id(); if (state_.contains(type_id) || extern_state_.contains(type_id)) { return get().diagnostics().ok(); } return true; } void PassContext::no_object_failure(int type_id) { // Rely on the type being available, otherwise how did we get here? LOG(BUG) << "get<" << type_names_[type_id] << "> failed; no object available."; __builtin_unreachable(); } PassManager &PassManager::add(Pass &&pass) { // Check that the inputs are all available. for (const int type_id : pass.inputs()) { if (!outputs_.contains(type_id)) { auto type_name = PassContext::type_names_[type_id]; LOG(BUG) << "Pass " << pass.name() << " requires output " << type_name << ", which is not available; it must be provided by a prior pass."; } } // Check that the registered output is unique. const int pass_id = passes_.size(); for (const int type_id : pass.outputs()) { if (outputs_.contains(type_id)) { auto &orig_pass = passes_[outputs_[type_id]]; auto type_name = PassContext::type_names_[type_id]; LOG(BUG) << "Pass " << pass.name() << " attempting to register output " << type_name << ", which is already registered by pass " << orig_pass.name() << "."; } // Register the output. outputs_.emplace(type_id, pass_id); } // Add the actual pass. passes_.emplace_back(std::move(pass)); return *this; } PassManager &PassManager::add(std::vector &&passes) { for (auto &pass : passes) { add(std::move(pass)); } return *this; } Result<> PassManager::foreach(std::function(const Pass &)> fn) { for (const auto &pass : passes_) { auto err = fn(pass); if (!err) { return err; } } return OK(); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/pass_manager.h000066400000000000000000000274071506776124200175770ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include "util/result.h" #include "util/type_name.h" namespace bpftrace::ast { // PassContext holds the intermediate state of executing passes. // // This class is essentially a type-erased generic constant storage. class PassContext { public: // TypeId is a class which, when specialized, returns the type_id. This // works by storing a static variable for each specialization of the class, // which effectively memoizes an ID binding for the specific class. template struct TypeId { static int type_id() { // Register by dispatching via `type_name` for the passed type. This is // all going to be devirtualized most likely, but this does enforce that // the class passed at least implements the static method, which is // really all we can ask for. // // Note that the `static` here is doing all the heavy lifting; this is // ensuring that the compiler is linking this symbol at most once, and it // will be initialized at most once, giving us a unique identifier for // this particular type. static_assert(std::is_base_of_v, "type must be derived from State<...>"); static int type_id = PassContext::add_type(T::type_name()); return type_id; } }; // State is an object which is the result of some specific pass. // // Passes are allowed to mutate the AST, and produce intermediate results // that may be consumed by other passes. Each of these results must be a // State, and will be immutable once it is produced. class State { public: virtual ~State() = default; }; // get fetches the given type; which must have been previously stored. // // N.B. In the future, these objects should be made immutable. Right now, // this is preserved so that we can pass through the `BPFTrace` object which // is consumed by *most* passes in a read-write fashion. template T &get() const { int type_id = TypeId::type_id(); auto it = state_.find(type_id); if (it != state_.end()) { return *static_cast(it->second.get()); } auto extern_it = extern_state_.find(type_id); if (extern_it != extern_state_.end()) { return static_cast(extern_it->second.get()); } no_object_failure(type_id); } // has indicates whether the given state is present or not. template bool has() { int type_id = TypeId::type_id(); return state_.contains(type_id) || extern_state_.contains(type_id); } private: // for the failed lookup path, see above. [[noreturn]] static void no_object_failure(int type_id); // put emplaces the value into the map; it must not exist already. In general // this should not be called manually, it should only be called by individual // passes during execution (the `run` function below). template void put(T &&t) { int type_id = TypeId::type_id(); state_.emplace(type_id, std::make_unique(std::move(t))); } // put can also provide an external reference for the map; its lifecycle is // managed outside the scope of the passes. While passes can produce results // that are references, this should be generally discouraged (and this // capability should eventually be removed). Like `put`, this should not be // called directly and should only be called by `run`, below. template void put(T &t) { int type_id = TypeId::type_id(); extern_state_.emplace(type_id, t); } // Will register the given type id and the name, which is resolvable via // the `type_names_` map. This is called exclusively by `type_id` above. static int add_type(std::string &&type_name) { int type_id = next_type_id_++; type_names_[type_id] = std::move(type_name); return type_id; } // Provide a short-hand to determine: // - Whether the context contains an `ASTContext`, and, // - Whether this `ASTContext` has error diagnostics. // // If both of the above are true, then false is returned. This is a // convenience method provided for the `PassManager` below, as passes will // stop when an error diagnostic has been generated. bool ok() const; static std::atomic next_type_id_; static std::unordered_map type_names_; std::unordered_map> state_; std::unordered_map> extern_state_; friend class PassManager; friend class Pass; }; // State is the template class used for states that may be stored in a // PassContext. In order to consume or produce information in a given pass, // simply override this class with the desired type. // // For example: // // class Variables : State<"variables"> { // // my fields // }; // // The `Variables` class can then be stored in the pass context. template class State : public PassContext::State { public: static std::string type_name() { return name.str(); } }; // Pass is an arbitrary pass. // // It is permitted to produce either no output or a single output, which may be // wrapped in `Result`, and may consume zero, one or multiple inputs. // // In general, this type should be constructed automatically, e.g.: // // Pass CreateFooPass() { // return Pass::create("mypass", [](MyInput &input) { // return MyOutput(); // }); // } // // Pass functions must already return an `Result` instance. class Pass { public: Pass(const Pass &) = delete; Pass(Pass &&) = default; Pass &operator=(const Pass &) = delete; Pass &operator=(Pass &&) = default; // create is the standard Pass constructor. // // For convenience, this template is set up to match against lambdas, and the // partial specialization versions of `createFn` below are used to match // against the resulting std::function. (Of course, a std::function can also // be passed here and the same applies, but using a lambda is encouraged.) template static Pass create(const std::string &name, Func func) { std::function fn(func); return createFn(name, fn); } // run executes the pass. // // The inputs must be present in the context. Result<> run(PassContext &ctx) const { return fn_(ctx); } const std::string &name() const { return name_; } const std::vector &inputs() const { return inputs_; } const std::vector &outputs() const { return outputs_; } private: template static Pass createFn(const std::string &name, std::function(Inputs &...)> fn) { return createImpl(name, fn); } template static Pass createFn(const std::string &name, std::function fn) { // We need to ensure that the arguments are references, but can otherwise // simply wrap this in something that returns OK on the user's behalf. static_assert((std::is_reference_v && ...), "all arguments must be references"); if constexpr (std::is_same_v) { std::function(Inputs...)> wrap( [fn](Inputs... inputs) -> Result { fn(inputs...); return OK(); }); return createImpl(name, wrap); } else { std::function(Inputs...)> wrap( [fn](Inputs... inputs) -> Result { return fn(inputs...); }); return createImpl(name, wrap); } } template static Pass createImpl(const std::string &name, std::function(Inputs &...)> fn) { std::vector inputs = { PassContext::TypeId::type_id()... }; std::vector outputs; if constexpr (!std::is_same_v) { // Note that the Return value may be a reference, so we resolve the // `type_id` of the underlying type. The Input types are not references, // because of the way the template is constructed. outputs = { PassContext::TypeId>::type_id() }; } return Pass( name, std::move(inputs), std::move(outputs), [fn](PassContext &ctx) -> Result<> { // Extract a tuple of all input parameters, apply the function // and set the result. auto args = std::make_tuple(std::ref(ctx.get())...); auto result = std::apply(fn, args); if (!result) { return result.takeError(); } if constexpr (std::is_same_v) { return OK(); } else { static_assert( std::is_base_of_v>, "return value must be derived from State<...>"); if constexpr (std::is_reference_v) { // Add the external reference. ctx.put(*result); } else { // Just move the result. ctx.put(std::move(*result)); } return OK(); } }); } // N.B. external construction is done via `create`. using Fn = std::function(PassContext &ctx)>; Pass(std::string name, std::vector &&inputs, std::vector &&outputs, Fn fn) : name_(std::move(name)), inputs_(std::move(inputs)), outputs_(std::move(outputs)), fn_(std::move(fn)) { } std::string name_; std::vector inputs_; std::vector outputs_; Fn fn_; }; // PassManager holds passes in sequence. // // The PassManager is responsible for verifying the inputs and outputs of the // passes ahead of time, so that the produced graph is sound. class PassManager { public: // registers the given pass, adding to the current list. PassManager &add(Pass &&pass); // registers all the given passes. PassManager &add(std::vector &&passes); // foreach executes the given function on every pass. // // The first pass to encounter errors will result in execution stopping. All // warnings from all passes are collected. Result<> foreach(std::function(const Pass &)> fn); // put adds a pass which always pushes the given static data. template PassManager &put(T &t) { return add(Pass::create(T::type_name(), [&t]() -> T & { return t; })); } // run all registered passes. // // This is a canonical example of how to use `foreach` above, in many other // cases a direct call will be suitable. // // Even if the `PassContext` is returned, if an ASTContext is registered, the // user should ensure that no error diagnostics have been produced prior to // extracting state from the context. In this case, it is possible that not // all passes have been run, even if none produced an error. Result run() { PassContext ctx; auto err = foreach([&](auto &pass) -> Result<> { // See above: for convenience, once error diagnostics have been // registered, we skip all remaining passes. This helps to ensure that // users are not overwhelmed by diagnostics, and encodes a common pattern // without the need for explicit error plumbing. if (!ctx.ok()) return OK(); return pass.run(ctx); }); if (!err) { return err.takeError(); } return ctx; } private: // The set of registered passes, in order. std::vector passes_; // The set of registered outputs in aggregate for all other passes. Note that // this is filled in and automatically checked by add, above. The value of // this map is the index into `passes_` above. std::unordered_map outputs_; }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/000077500000000000000000000000001506776124200162525ustar00rootroot00000000000000bpftrace-0.24.1/src/ast/passes/c_macro_expansion.cpp000066400000000000000000000042731506776124200224530ustar00rootroot00000000000000#include #include "ast/ast.h" #include "ast/context.h" #include "ast/passes/c_macro_expansion.h" #include "ast/visitor.h" #include "clang_parser.h" #include "driver.h" #include "util/strings.h" namespace bpftrace::ast { class CMacroExpander : public Visitor { public: CMacroExpander(ASTContext &ast, CDefinitions &c_definitions) : ast_(ast), c_definitions_(c_definitions) {}; using Visitor::visit; void visit(Expression &expr); private: ASTContext &ast_; CDefinitions &c_definitions_; std::vector active_; }; void CMacroExpander::visit(Expression &expr) { // N.B. We only support raw identifier macros. The way expansion works is // that we see if an expression is a bare identifier, then attempt expansion // recurisvely. if (auto *ident = expr.as()) { if (c_definitions_.macros.contains(ident->ident)) { const auto &value = c_definitions_.macros[ident->ident]; // Check for recursion. if (std::ranges::find(active_, ident->ident) != active_.end()) { ident->addError() << "Macro recursion: " << util::str_join(active_, "->"); return; } // Parse just the macro as an expression. ASTContext macro(ident->ident, value); Driver driver(macro); auto expanded = driver.parse_expr(); if (!expanded) { ident->addError() << "unable to expand macro as an expression: " << value; return; } // Expand the macro expression in place. expr.value = clone(ast_, expanded->value, ident->loc); // Recursively visit the potentially expanded expression, ensuring that // we can catch recursive expansion, per above. active_.emplace_back(ident->ident); Visitor::visit(expr); active_.pop_back(); return; } } // Expand normally. Visitor::visit(expr); } Pass CreateCMacroExpansionPass() { auto fn = [](ASTContext &ast, CDefinitions &c_definitions) { CMacroExpander expander(ast, c_definitions); expander.visit(ast.root); }; return Pass::create("CMacroExpansion", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/c_macro_expansion.h000066400000000000000000000002101506776124200221030ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateCMacroExpansionPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/clang_build.cpp000066400000000000000000000177631506776124200212370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LLVM_VERSION_MAJOR <= 16 #include #endif #include "arch/arch.h" #include "ast/ast.h" #include "ast/passes/clang_build.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/resolve_imports.h" #include "bpftrace.h" #include "stdlib/stdlib.h" #include "util/memfd.h" #include "util/result.h" namespace bpftrace::ast { char ClangBuildError::ID; void ClangBuildError::log(llvm::raw_ostream &OS) const { OS << msg_; } static Result<> build(CompileContext &ctx, const std::string &name, LoadedObject &obj, const llvm::MemoryBufferRef &vmlinux_h, Imports &imports, BitcodeModules &result) { llvm::IntrusiveRefCntPtr vfs( new llvm::vfs::InMemoryFileSystem()); vfs->addFile(name, 0, llvm::MemoryBuffer::getMemBufferCopy(obj.data(), "main")); const std::string asm_dir = "include/asm/" + arch::Host::asm_arch() + "/"; for (const auto &[name, other] : stdlib::Stdlib::files) { // If this include file is an arch-specific assembly file, then we // skip if it does not match the current architecture. If it *does* // match the current architecture, then we remap it as the `asm` // directory (without the arch prefix). if (name.starts_with("include/asm/")) { if (!name.starts_with(asm_dir)) { continue; // Not our architecture. } // Replace the arch-specific path with just the asm path. std::string nn = "include/asm/" + name.substr(asm_dir.size()); vfs->addFile(nn, 0, llvm::MemoryBuffer::getMemBufferCopy(other, nn)); } else { vfs->addFile(name, 0, llvm::MemoryBuffer::getMemBufferCopy(other, name)); } } for (auto &[name, other] : imports.c_headers) { vfs->addFile(name, 0, llvm::MemoryBuffer::getMemBufferCopy(other.data(), name)); } vfs->addFileNoOwn("include/vmlinux.h", 0, vmlinux_h); // Create the diagnostic options and client. We emit the error to // a string, which we can then capture and associate with the import. std::string errstr; llvm::raw_string_ostream err(errstr); #if LLVM_VERSION_MAJOR < 21 auto diagOpts = llvm::makeIntrusiveRefCnt(); auto diags = std::make_unique( llvm::makeIntrusiveRefCnt(), diagOpts, new clang::TextDiagnosticPrinter(err, diagOpts.get())); #else // Clang 21: DiagnosticOptions is NOT intrusive-refcounted anymore. // Keep it alive for the program lifetime (or store it on a longer-lived // object). static std::shared_ptr diagOpts = std::make_shared(); llvm::IntrusiveRefCntPtr diagID( new clang::DiagnosticIDs()); auto client = std::make_unique(err, *diagOpts); auto diags = std::make_unique(diagID, *diagOpts, client.release()); #endif // We create a temporary memfd that we can use to store the output, // since the ClangDriver API is framed in terms of filenames. Perhaps // we could use the internals here, but that carries other risks. auto memfd = util::MemFd::create(name); if (!memfd) { return memfd.takeError(); } // Create the compiler invocation. Note that the `-O2` introduces some passes // that seem to be load-bearing with respect to generating useful debug // information, for some reason. The generated module will be linked and // optimized again regardless, but it is better safe than sorry. std::vector args; args.push_back("-O2"); args.push_back("-Iinclude"); for (const auto &s : arch::Host::c_defs()) { args.push_back("-D"); args.push_back(s.c_str()); } args.push_back("-o"); args.push_back(memfd->path().c_str()); args.push_back(name.c_str()); // Configure the instance. We want to read the source file named // by `name` above, enable debug information and optimization. auto inv = std::make_shared(); clang::CompilerInvocation::CreateFromArgs(*inv, llvm::ArrayRef(args), *diags); inv->getTargetOpts().Triple = "bpf"; #if LLVM_VERSION_MAJOR <= 16 inv->getCodeGenOpts().setDebugInfo(clang::codegenoptions::FullDebugInfo); #else inv->getCodeGenOpts().setDebugInfo(llvm::codegenoptions::FullDebugInfo); #endif inv->getCodeGenOpts().DebugColumnInfo = true; clang::CompilerInstance ci; // Cross-version friendly: assign into the existing invocation // (works across modern Clang majors, including 21) ci.getInvocation() = *inv; ci.setDiagnostics(diags.release()); ci.setFileManager(new clang::FileManager(clang::FileSystemOptions(), vfs)); ci.createSourceManager(ci.getFileManager()); // Generate the object file, which should include the required BTF // debug information. This also generates the module as a // side-effect, which is what we actually extract for linking. std::unique_ptr action = std::make_unique(ctx.context.get()); if (!ci.ExecuteAction(*action)) { // This is likely a build failure, we can surface this directly // into the user context. We first highlight the location of the // original import, then include the C message as a "hint". auto &e = obj.node.addError(); e << "failed to build"; e.addHint() << errstr; return OK(); } if (!errstr.empty()) { // If the compilation didn't fail, then these weren't errors but we // can surface them as compilation warnings. auto &e = obj.node.addWarning(); e << "found external warnings"; e.addHint() << errstr; } std::unique_ptr mod = action->takeModule(); if (!mod) { // This is an internal error, not suitable to surface as a user // diagnostic. Surface it directly as an error in the pipeline. return make_error("failed to generate module"); } auto data = memfd->read_all(); if (!data) { return data.takeError(); } result.modules.emplace_back(std::move(mod)); result.objects.emplace_back(std::move(*data)); return OK(); } ast::Pass CreateClangBuildPass() { return ast::Pass::create( "ClangBuilder", [](BPFtrace &bpftrace, CompileContext &ctx, ast::Imports &imports) -> Result { BitcodeModules result; // Nothing to do? Return directly. if (imports.c_sources.empty()) { return result; } // Construct our kernel headers. This is a rather expensive operation, // so we ensure that we do this only once for all files. std::string vmlinux_h = bpftrace.btf_->c_def(); // For each of the source files in the imports, we // build it and turn it into a bitcode file. for (auto &[name, obj] : imports.c_sources) { auto ok = build(ctx, name, obj, llvm::MemoryBufferRef(llvm::StringRef(vmlinux_h), "vmlinux.h"), imports, result); if (!ok) { return ok.takeError(); } } return result; }); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/clang_build.h000066400000000000000000000011051506776124200206630ustar00rootroot00000000000000#pragma once #include #include #include "ast/pass_manager.h" namespace bpftrace::ast { class BitcodeModules : public State<"bitcode"> { public: std::vector> modules; std::vector objects; }; class ClangBuildError : public ErrorInfo { public: static char ID; void log(llvm::raw_ostream &OS) const override; ClangBuildError(std::string msg) : msg_(std::move(msg)) {}; private: std::string msg_; }; ast::Pass CreateClangBuildPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/clang_parser.cpp000066400000000000000000000731761506776124200214340ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ast/ast.h" #include "ast/context.h" #include "ast/passes/resolve_imports.h" #include "bpftrace.h" #include "btf.h" #include "clang_parser.h" #include "log.h" #include "stdlib/stdlib.h" #include "types.h" #include "util/io.h" #include "util/strings.h" #include "util/system.h" namespace bpftrace::ast { char ClangParseError::ID; void ClangParseError::log(llvm::raw_ostream &OS) const { OS << "Clang parse error"; } class ClangParser { public: bool parse(ast::Program *program, BPFtrace &bpftrace, std::vector extra_flags = {}); // Moved out by the pass. CDefinitions definitions; 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); // 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 { const std::vector &getDefaultHeaders() { // N.B. the `cached` value here hangs on to a vector of strings, as well as a // vector of `CXUnsavedFile` objects. Since the `CXUnsavedFile` objects need // a C-style string, this is a `.c_str()` that comes from one of the saved // names. It is important that these objects are kept alive, even if they are // not returned from this function. static auto cached = [] { std::vector names; std::vector unsaved_files; for (const auto &[name, view] : stdlib::Stdlib::files) { if (!name.ends_with(".h")) { continue; // Inlucde only headers. } const auto &n = names.emplace_back("/bpftrace/" + name); unsaved_files.push_back(CXUnsavedFile{ .Filename = n.c_str(), .Contents = view.data(), .Length = view.length(), }); } return std::make_pair(std::move(names), std::move(unsaved_files)); }(); return cached.second; // See above. } 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.empty(); } 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) { // See btf.cpp; we need to signal well-formedness. return CreateString(size + 1); } 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); CXString msg_str = clang_getDiagnosticSpelling(diag); auto &msg = error_msgs.emplace_back(clang_getCString(msg_str)); clang_disposeString(msg_str); if ((bail_on_error && severity == CXDiagnostic_Error) || severity == CXDiagnostic_Fatal) { // Do not fail on "too many errors" return !bail_on_error && msg == "too many errors emitted, stopping now"; } } 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) { util::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() { return std::ranges::any_of(error_msgs, [](const auto &msg) { return msg.find("redefinition") != std::string::npos; }); } bool ClangParser::ClangParserHandler::has_unknown_type_error() { return std::ranges::any_of(error_msgs, [](const auto &msg) { return ClangParser::get_unknown_type(msg).has_value(); }); } namespace { using visitFn = std::function; int visitChildren(CXCursor cursor, visitFn fn) { return clang_visitChildren( cursor, [](CXCursor c, CXCursor parent, CXClientData data) { auto *cb = static_cast(data); return (*cb)(c, parent); }, static_cast(&fn)); } // Get annotation associated with field declaration `c` std::optional get_field_decl_annotation(CXCursor c) { assert(clang_getCursorKind(c) == CXCursor_FieldDecl); std::optional annotation; visitChildren(c, [&](CXCursor c, CXCursor __attribute__((unused)) parent) { // 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) { annotation = get_clang_string(clang_getCursorSpelling(c)); } return CXChildVisit_Recurse; }); return annotation; } } // namespace bool ClangParser::visit_children(CXCursor &cursor, BPFtrace &bpftrace) { int err = visitChildren(cursor, [&](CXCursor c, CXCursor parent) { if (clang_getCursorKind(c) == CXCursor_MacroDefinition) { std::string macro_name; std::string macro_value; if (translateMacro(c, macro_name, macro_value)) { definitions.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 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 = name.str(); } auto variant_name = get_clang_string(clang_getCursorSpelling(c)); auto variant_value = clang_getEnumConstantDeclValue(c); definitions.enums[variant_name] = std::make_pair(variant_value, enum_name); // Store enum name to variant value to variant name definitions.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) { // N.B. In the future this may be moved into the C definitions, but // currently this is rather tied in to a lot of other plumbing. auto &structs = bpftrace.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. str->AddField(ident, sized_type, offset, bitfield, is_data_loc); } return CXChildVisit_Recurse; }); // 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(); visitChildren(cursor, [&](CXCursor c, CXCursor parent) { // 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)); type_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 (!type_data.complete_types.contains(type_name)) type_data.incomplete_types.emplace(std::move(type_name)); } } return CXChildVisit_Recurse; }); return type_data.incomplete_types; } void ClangParser::resolve_incomplete_types_from_btf(BPFtrace &bpftrace) { std::unordered_set last_incomplete; while (true) { // Collect incomplete types and retrieve their definitions from BTF. auto incomplete_types = get_incomplete_types(); // No need to continue if nothing is incomplete. if (incomplete_types.empty()) { break; } // It is an error to attempt to continue if we've converge on a set of // incomplete types which is not changing. if (incomplete_types == last_incomplete) { break; } bpftrace.btf_set_.insert(incomplete_types.cbegin(), incomplete_types.cend()); input_files.back() = get_btf_generated_header(bpftrace); last_incomplete = std::move(incomplete_types); } } // 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.., 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) { 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); 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 (const auto &unknown_type_msg : unknown_type_msgs) { if (diagnostic_msg.starts_with(unknown_type_msg)) { 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) { // Note that `c_def` will provide the full set of types if an empty set is // used here. We only want the types in `btf_set_` or nothing at all, so // don't generate anything if this set is empty. if (!bpftrace.btf_set_.empty()) { 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) { auto clang = "clang-" + std::to_string(LLVM_VERSION_MAJOR); auto cmd = clang + " -Wp,-v -x c -fsyntax-only /dev/null 2>&1"; auto check = util::exec_system(cmd.c_str()); if (!check) { // Exec failed, ignore and move on. return; } 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(util::trim(line)); } 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(util::trim(line)); } if (result.empty()) result = { "/usr/local/include", "/usr/include" }; return result; } ast::Pass CreateClangParsePass(std::vector &&extra_flags) { return ast::Pass::create("ClangParser", [extra_flags = std::move(extra_flags)]( ast::ASTContext &ast, BPFtrace &b) -> Result { ClangParser parser; if (!parser.parse(ast.root, b, extra_flags)) { return make_error(); } return std::move(parser.definitions); }); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/clang_parser.h000066400000000000000000000015701506776124200210660ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" #include namespace bpftrace::ast { // When the imported definitions are parsed with clang, relevant C definitions // are centralized here to be consumed by later passes. class CDefinitions : public ast::State<"C-definitions"> { public: // Map of macro name to macro definition. 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; }; class ClangParseError : public ErrorInfo { public: static char ID; void log(llvm::raw_ostream &OS) const override; }; ast::Pass CreateClangParsePass(std::vector &&extra_flags = {}); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/codegen_llvm.cpp000066400000000000000000006343221506776124200214260ustar00rootroot00000000000000#include #include #include #include #include #include // Required for LLVM_VERSION_MAJOR. #include #if LLVM_VERSION_MAJOR <= 16 #include #endif #include #include #include #include #include #include #include #include #include #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/async_ids.h" #include "ast/codegen_helper.h" #include "ast/context.h" #include "ast/dibuilderbpf.h" #include "ast/irbuilderbpf.h" #include "ast/location.h" #include "ast/passes/clang_build.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/link.h" #include "ast/passes/named_param.h" #include "ast/passes/probe_expansion.h" #include "ast/signal_bt.h" #include "ast/visitor.h" #include "async_action.h" #include "bpfmap.h" #include "bpftrace.h" #include "codegen_resources.h" #include "format_string.h" #include "globalvars.h" #include "kfuncs.h" #include "log.h" #include "map_info.h" #include "required_resources.h" #include "tracepoint_format_parser.h" #include "types.h" #include "usdt.h" #include "util/bpf_names.h" #include "util/cgroup.h" #include "util/cpus.h" #include "util/exceptions.h" namespace bpftrace::ast { using namespace llvm; static constexpr char LLVMTargetTriple[] = "bpf"; static constexpr auto LICENSE = "LICENSE"; static auto getTargetMachine() { static auto *target = []() { std::string error_str; const auto *target = llvm::TargetRegistry::lookupTarget(LLVMTargetTriple, error_str); if (!target) { throw util::FatalUserException( "Could not find bpf llvm target, does your llvm support it?"); } auto *machine = target->createTargetMachine( #if LLVM_VERSION_MAJOR >= 21 Triple(LLVMTargetTriple), #else LLVMTargetTriple, #endif "generic", "", TargetOptions(), std::optional()); #if LLVM_VERSION_MAJOR >= 18 machine->setOptLevel(llvm::CodeGenOptLevel::Aggressive); #else machine->setOptLevel(llvm::CodeGenOpt::Aggressive); #endif return machine; }(); return target; } static bool shouldForceInitPidNs(const ExpressionList &args) { return args.size() == 1 && args.at(0).as()->ident == "init"; } namespace { class InternalError : public ErrorInfo { public: InternalError(std::string msg) : msg_(std::move(msg)) {}; static char ID; void log(llvm::raw_ostream &OS) const override { OS << msg_; } private: std::string msg_; }; char InternalError::ID; 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() = default; // 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 &ast, BPFtrace &bpftrace, CDefinitions &c_definitions, NamedParamDefaults &named_param_defaults, LLVMContext &llvm_ctx, USDTHelper &usdt_helper, ExpansionResult &expansions); using Visitor::visit; ScopedExpr visit(Integer &integer); ScopedExpr visit(NegativeInteger &integer); ScopedExpr visit(Boolean &boolean); ScopedExpr visit(String &string); ScopedExpr visit(Identifier &identifier); ScopedExpr visit(Builtin &builtin); ScopedExpr visit(Call &call); ScopedExpr visit(Map &map); ScopedExpr visit(MapAddr &map_addr); ScopedExpr visit(Variable &var); ScopedExpr visit(VariableAddr &var_addr); ScopedExpr visit(Binop &binop); ScopedExpr visit(Unop &unop); ScopedExpr visit(Ternary &ternary); ScopedExpr visit(FieldAccess &acc); ScopedExpr visit(ArrayAccess &arr); ScopedExpr visit(TupleAccess &acc); ScopedExpr visit(MapAccess &acc); 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, Map &map); ScopedExpr visit(For &f, Range &range); ScopedExpr visit(For &f); ScopedExpr visit(Jump &jump); ScopedExpr visit(Predicate &pred); ScopedExpr visit(Probe &probe); ScopedExpr visit(Subprog &subprog); ScopedExpr visit(Program &program); ScopedExpr visit(Block &block); ScopedExpr visit(BlockExpr &block_expr); // compile is the primary entrypoint; it will return the generated LLVMModule. // Only one call to `compile` is permitted per instantiation. std::unique_ptr compile(); private: int getNextIndexForProbe(); ScopedExpr createLogicalAnd(Binop &binop); ScopedExpr createLogicalOr(Binop &binop); void createFormatStringCall(Call &call, int id, const std::vector &call_args, const std::string &call_name, async_action::AsyncAction async_action); void createPrintMapCall(Call &call); void createPrintNonMapCall(Call &call, int id); void createJoinCall(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_maps(const RequiredResources &required_resources, const CodegenResources &codegen_resources); void generate_global_vars(const RequiredResources &resources, const ::bpftrace::Config &bpftrace_config); // 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 &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, FunctionType *func_type); [[nodiscard]] ScopedExpr getMapKey(Map &map, Expression &key_expr); [[nodiscard]] ScopedExpr getMultiMapKey( Map &map, Expression &key_expr, 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. Result<> generateWatchpointSetupProbe(FunctionType *func_type, const std::string &expanded_probe_name, int arg_num, int index, const Location &loc); 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, const Location &loc, const std::string &temp_name); ScopedExpr createIncDec(Unop &unop); llvm::Function *createMapLenCallback(); // This function creates the context type and value for callbacks. Extra // fields for this context may be passed as the `extra_fields` argument. // // The context created here is suitable for use in `createForCallback`. std::pair createForContext( const For &f, std::vector &&extra_fields = {}); // This creates and invokes a callback function that captures all required // arguments in the context type. Note that all callbacks require some // argument for the context; this is found by finding a parameter named `ctx` // in the `debug_arg`. This must be provided by the caller. // // The provided `decl` function is invoked to construct the scoped value for // the local context. llvm::Function *createForCallback( For &f, const std::string &name, ArrayRef args, const Struct &debug_args, llvm::Type *ctx_t, std::function decl); llvm::Function *createForEachMapCallback(const For &f, const Map &map, llvm::Type *ctx_t); llvm::Function *createMurmurHash2Func(); Value *createFmtString(int print_id); bool canAggPerCpuMapElems(libbpf::bpf_map_type map_type, const SizedType &val_type); void maybeAllocVariable(const std::string &var_ident, const SizedType &var_type, const Location &loc); VariableLLVM *maybeGetVariable(const std::string &var_ident); VariableLLVM &getVariable(const std::string &var_ident); llvm::Function *DeclareKernelFunc(Kfunc kfunc, Node &call); CallInst *CreateKernelFuncCall(Kfunc kfunc, ArrayRef args, const Twine &name, Node &call); GlobalVariable *DeclareKernelVar(const std::string &name); ASTContext &ast_; BPFtrace &bpftrace_; CDefinitions &c_definitions_; NamedParamDefaults &named_param_defaults_; LLVMContext &llvm_ctx_; USDTHelper &usdt_helper_; ExpansionResult &expansions_; std::unique_ptr module_; AsyncIds async_ids_; IRBuilderBPF b_; DIBuilderBPF debug_; const DataLayout &datalayout() const { return module_->getDataLayout(); } Value *ctx_; llvm::DILocalScope *scope_ = nullptr; AttachPoint *current_attach_point_ = nullptr; std::string probefull_; 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_; std::unordered_map extern_funcs_; llvm::Value *createGetNsSwTAI(const Location &loc); }; } // namespace CodegenLLVM::CodegenLLVM(ASTContext &ast, BPFtrace &bpftrace, CDefinitions &c_definitions, NamedParamDefaults &named_param_defaults, LLVMContext &llvm_ctx, USDTHelper &usdt_helper, ExpansionResult &expansions) : ast_(ast), bpftrace_(bpftrace), c_definitions_(c_definitions), named_param_defaults_(named_param_defaults), llvm_ctx_(llvm_ctx), usdt_helper_(usdt_helper), expansions_(expansions), module_(std::make_unique("bpftrace", llvm_ctx)), b_(llvm_ctx, *module_, bpftrace, async_ids_), debug_(*module_) { #if LLVM_VERSION_MAJOR >= 21 module_->setTargetTriple(Triple(LLVMTargetTriple)); #else module_->setTargetTriple(LLVMTargetTriple); #endif module_->setDataLayout(getTargetMachine()->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); // The unwind table causes problems when linking via libbpf. module_->setUwtable(llvm::UWTableKind::None); // Set license of BPF programs. const std::string &license = bpftrace_.config_->license; auto license_size = license.size() + 1; auto *license_var = llvm::dyn_cast( module_->getOrInsertGlobal(LICENSE, ArrayType::get(b_.getInt8Ty(), license_size))); license_var->setInitializer( ConstantDataArray::getString(module_->getContext(), license)); license_var->setSection("license"); license_var->addDebugInfo( debug_.createGlobalVariable(LICENSE, CreateString(license_size))); } ScopedExpr CodegenLLVM::visit(Integer &integer) { return ScopedExpr(b_.getInt64(integer.value)); } ScopedExpr CodegenLLVM::visit(NegativeInteger &integer) { return ScopedExpr(b_.getInt64(integer.value)); } ScopedExpr CodegenLLVM::visit(Boolean &boolean) { return ScopedExpr(b_.getInt1(boolean.value)); } ScopedExpr CodegenLLVM::visit(String &string) { std::string s(string.value); auto *string_var = llvm::dyn_cast(module_->getOrInsertGlobal( s, ArrayType::get(b_.getInt8Ty(), string.string_type.GetSize()))); string_var->setInitializer( ConstantDataArray::getString(module_->getContext(), s)); 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 (c_definitions_.enums.contains(identifier.ident)) { return ScopedExpr( b_.getInt64(std::get<0>(c_definitions_.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( 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(loc, false), 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 == "__builtin_elapsed") { AllocaInst *key = b_.CreateAllocaBPF(b_.getInt64Ty(), "elapsed_key"); b_.CreateStore(b_.getInt64(0), key); auto type = CreateUInt64(); auto *start = b_.CreateMapLookupElem( 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.builtin_type.stack_type, builtin.loc); } else if (builtin.ident == "pid") { return ScopedExpr(b_.CreateGetPid(builtin.loc, false)); } else if (builtin.ident == "tid") { return ScopedExpr(b_.CreateGetTid(builtin.loc, false)); } else if (builtin.ident == "__builtin_cgroup") { return ScopedExpr(b_.CreateGetCurrentCgroupId(builtin.loc)); } else if (builtin.ident == "__builtin_uid" || builtin.ident == "__builtin_gid" || builtin.ident == "__builtin_username") { Value *uidgid = b_.CreateGetUidGid(builtin.loc); if (builtin.ident == "__builtin_uid" || builtin.ident == "__builtin_username") { return ScopedExpr(b_.CreateAnd(uidgid, 0xffffffff)); } else if (builtin.ident == "__builtin_gid") { return ScopedExpr(b_.CreateLShr(uidgid, 32)); } __builtin_unreachable(); } else if (builtin.ident == "__builtin_usermode") { if (arch::Host::Machine == arch::Machine::X86_64) { auto cs_offset = arch::Host::register_to_pt_regs_offset("cs"); if (!cs_offset) { builtin.addError() << "No CS register?"; return ScopedExpr(b_.getInt64(0)); } Value *cs = b_.CreateRegisterRead(ctx_, cs_offset.value(), "reg_cs"); Value *mask = b_.getInt64(0x3); Value *is_usermode = b_.CreateICmpEQ(b_.CreateAnd(cs, mask), b_.getInt64(3), "is_usermode"); Value *expr = b_.CreateZExt(is_usermode, b_.GetType(builtin.builtin_type), "usermode_result"); return ScopedExpr(expr); } else { // We lack an implementation. builtin.addError() << "not supported on architecture " << arch::Host::Machine; return ScopedExpr(b_.getInt64(0)); } } else if (builtin.ident == "__builtin_numaid") { return ScopedExpr(b_.CreateGetNumaId(builtin.loc)); } else if (builtin.ident == "__builtin_cpu") { Value *cpu = b_.CreateGetCpuId(builtin.loc); return ScopedExpr(b_.CreateZExt(cpu, b_.getInt64Ty())); } else if (builtin.ident == "__builtin_ncpus") { return ScopedExpr(b_.CreateLoad(b_.getInt64Ty(), module_->getGlobalVariable(std::string( bpftrace::globalvars::NUM_CPUS)), "num_cpu.cmp")); } else if (builtin.ident == "__builtin_curtask") { return ScopedExpr(b_.CreateGetCurrentTask(builtin.loc)); } else if (builtin.ident == "__builtin_rand") { Value *random = b_.CreateGetRandom(builtin.loc); return ScopedExpr(b_.CreateZExt(random, b_.getInt64Ty())); } else if (builtin.ident == "__builtin_comm") { AllocaInst *buf = b_.CreateAllocaBPF(builtin.builtin_type, "__builtin_comm"); // initializing memory needed for older kernels: b_.CreateMemsetBPF(buf, b_.getInt8(0), builtin.builtin_type.GetSize()); b_.CreateGetCurrentComm(buf, builtin.builtin_type.GetSize(), builtin.loc); return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); } else if (builtin.ident == "__builtin_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.builtin_type.IsUsymTy()) { value = b_.CreateUSym(value, get_probe_id(), builtin.loc); return ScopedExpr(value, [this, value]() { b_.CreateLifetimeEnd(value); }); } return ScopedExpr(value); } else if (builtin.is_argx() || builtin.ident == "__builtin_retval") { auto probe_type = probetype(current_attach_point_->provider); if (builtin.builtin_type.is_funcarg) { return ScopedExpr( b_.CreateKFuncArg(ctx_, builtin.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.builtin_type.IsUsymTy()) { value = b_.CreateUSym(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') { auto sp_offset = arch::Host::register_to_pt_regs_offset( arch::Host::sp_value()); if (!sp_offset) { builtin.addError() << "no stack offset available"; return ScopedExpr(b_.getInt64(0)); } int arg_num = atoi(builtin.ident.substr(4).c_str()); Value *sp = b_.CreateRegisterRead(ctx_, sp_offset.value(), "reg_sp"); AllocaInst *dst = b_.CreateAllocaBPF(builtin.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()->getIntegerBitWidth() / 8; SizedType arg_type = CreatePointer(CreateInt8(), builtin.builtin_type.GetAS()); assert(builtin.builtin_type.GetSize() == arg_type.GetSize()); Value *src = b_.CreateAdd(sp, b_.getInt64((arg_num * arg_width) + arch::Host::argument_stack_offset())); b_.CreateProbeRead(dst, arg_type, src, builtin.loc); Value *expr = b_.CreateLoad(b_.GetType(builtin.builtin_type), dst); b_.CreateLifetimeEnd(dst); return ScopedExpr(expr); } else if (builtin.ident == "__builtin_probe") { auto probe_str = probefull_; probe_str.resize(builtin.builtin_type.GetSize() - 1); auto *probe_var = llvm::dyn_cast(module_->getOrInsertGlobal( probe_str, ArrayType::get(b_.getInt8Ty(), builtin.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.builtin_type)); } else if (builtin.ident == "args" || builtin.ident == "ctx") { // ctx is undocumented builtin: for debugging. return ScopedExpr(ctx_); } else if (builtin.ident == "__builtin_cpid") { pid_t cpid = bpftrace_.child_->pid(); if (cpid < 1) { LOG(BUG) << "Invalid cpid: " << cpid; } return ScopedExpr(b_.getInt64(cpid)); } else if (builtin.ident == "__builtin_jiffies") { return ScopedExpr(b_.CreateJiffies64(builtin.loc)); } else if (builtin.ident == "__builtin_session_is_return") { return ScopedExpr(CreateKernelFuncCall( Kfunc::bpf_session_is_return, {}, "is_return", builtin)); } else { LOG(BUG) << "unknown builtin \"" << builtin.ident << "\""; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::visit(Call &call) { if (call.func == "count") { Map &map = *call.vargs.at(0).as(); auto scoped_key = getMapKey(map, call.vargs.at(1)); b_.CreatePerCpuMapElemAdd( map, scoped_key.value(), b_.getInt64(1), call.loc); return ScopedExpr(); } else if (call.func == "sum") { Map &map = *call.vargs.at(0).as(); ScopedExpr scoped_key = getMapKey(map, call.vargs.at(1)); ScopedExpr scoped_expr = visit(call.vargs.at(2)); // promote int to 64-bit Value *cast = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), call.vargs.front().type().IsSigned()); b_.CreatePerCpuMapElemAdd(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.vargs.at(0).as(); ScopedExpr scoped_key = getMapKey(map, call.vargs.at(1)); CallInst *lookup = b_.CreateMapLookup(map, scoped_key.value()); ScopedExpr scoped_expr = visit(call.vargs.at(2)); // 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.value_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); b_.CreateMinMax( expr, b_.CreateGEP(mm_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(0) }), b_.CreateGEP(mm_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(1) }), is_max, map.value_type.IsSigned()); 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(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.vargs.at(0).as(); ScopedExpr scoped_key = getMapKey(map, call.vargs.at(1)); CallInst *lookup = b_.CreateMapLookup(map, scoped_key.value()); ScopedExpr scoped_expr = visit(call.vargs.at(2)); // promote int to 64-bit Value *expr = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), call.vargs.at(2).type().IsSigned()); llvm::Type *avg_struct_ty = b_.GetMapValueType(map.value_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(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.vargs.at(0).as(); ScopedExpr scoped_arg = visit(call.vargs.at(2)); // 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(3)); Value *k = b_.CreateIntCast(scoped_arg2.value(), b_.getInt64Ty(), false); // promote int to 64-bit Value *expr = b_.CreateIntCast(scoped_arg.value(), b_.getInt64Ty(), call.vargs.at(2).type().IsSigned()); Value *log2 = b_.CreateCall(log2_func_, { expr, k }, "log2"); ScopedExpr scoped_key = getMultiMapKey( map, call.vargs.at(1), { log2 }, call.loc); b_.CreatePerCpuMapElemAdd( 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.vargs.at(0).as(); // prepare arguments auto &value_arg = call.vargs.at(2); auto &min_arg = call.vargs.at(3); auto &max_arg = call.vargs.at(4); auto &step_arg = call.vargs.at(5); 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.at(2).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 = getMultiMapKey( map, call.vargs.at(1), { linear }, call.loc); b_.CreatePerCpuMapElemAdd( map, scoped_key.value(), b_.getInt64(1), call.loc); return ScopedExpr(); } else if (call.func == "tseries") { // tseries decides what the current bucket is based on the timestamp then // updates the bucket's value. // // void tseries(uint64_t n, uint64_t interval_ns, uint64_t num_intervals) { // uint64_t now = bpf_ktime_get_boot_ns(); // struct ts_struct ts_struct_alloc = {}; // uint64_t epoch = now / interval_ns; // uint64_t bucket = epoch % num_intervals; // struct ts_struct *bucket_value; // bool key_exists; // // bucket_value = bpf_map_lookup_elem(&tseries_map, &bucket); // if (!bucket_value) { // key_exists = false; // bucket_value = &ts_struct_alloc; // } else { // key_exists = true; // } // // #if defined(SUM) || defined(MIN) || defined(MAX) || defined(AVG) // if (epoch != bucket_value->epoch) { // bucket_value->value = 0; // bucket_value->meta = 0; // } // #endif // // #if defined(SUM) // bucket_value->value += n; // #elif defined(MIN) || defined(MAX) // if (!bucket_value->meta) { // bucket_value->value = n; // } else { // #if defined(MIN) // bucket_value->value = min(bucket_value->value, n); // #else // bucket_value->value = max(bucket_value->value, n); // #endif // } // bucket_value->meta = 1; // #elif defined(AVG) // bucket_value->value += n; // bucket_value->meta++; // #else // bucket_value->value = n; // bucket_value->meta = now; // #endif // // if (!key_exists) { // bpf_map_update_elem(&tseries_map, &bucket, bucket_value); // } // } Map &map = *call.vargs.at(0).as(); llvm::Function *parent = b_.GetInsertBlock()->getParent(); llvm::Type *ts_struct_ty = b_.GetMapValueType(map.type()); AllocaInst *ts_struct_ptr = b_.CreateAllocaBPF( PointerType::get(llvm_ctx_, 0), "ts_struct_ptr"); // Step 1) Figure out which bucket we're using. 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 &tseries_args = std::get(map_info->second.detail); Value *interval_ns = b_.getInt64(tseries_args.interval_ns); Value *num_intervals = b_.getInt64(tseries_args.num_intervals); Value *now = createGetNsSwTAI(call.loc); Value *epoch = b_.CreateUDiv(now, interval_ns); Value *bucket = b_.CreateURem(epoch, num_intervals); auto scoped_key = getMultiMapKey( map, call.vargs.at(1), { bucket }, call.loc); AllocaInst *key_exists = b_.CreateAllocaBPF(b_.getInt8Ty(), "key_exists"); // Step 2) If the bucket already exists in the map, assign ts_struct_ptr to // the result of the map lookup. Otherwise, assign ts_struct_ptr to // a local AllocaInst. CallInst *lookup = b_.CreateMapLookup(map, scoped_key.value()); BasicBlock *lookup_success_block = BasicBlock::Create(module_->getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_->getContext(), "lookup_failure", parent); BasicBlock *maybe_clear_block = BasicBlock::Create(module_->getContext(), "maybe_clear", parent); BasicBlock *merge_block = BasicBlock::Create(module_->getContext(), "merge", parent); BasicBlock *update_block = BasicBlock::Create(module_->getContext(), "update", parent); BasicBlock *exit_block = BasicBlock::Create(module_->getContext(), "exit", parent); Value *lookup_condition = b_.CreateICmpNE( b_.CreateIntCast(lookup, b_.getPtrTy(), true), b_.GetNull(), "map_lookup_cond"); b_.CreateCondBr(lookup_condition, lookup_success_block, lookup_failure_block); b_.SetInsertPoint(lookup_success_block); // Success: ts_struct_ptr just points to what's in the map. b_.CreateStore( b_.CreatePointerCast(lookup, PointerType::get(llvm_ctx_, 0), "cast"), ts_struct_ptr); b_.CreateStore(b_.getInt8(1), key_exists); b_.CreateBr(maybe_clear_block); b_.SetInsertPoint(lookup_failure_block); // Failure: ts_struct_ptr points to a zero-initialized ts_struct_ty. AllocaInst *ts_struct = b_.CreateAllocaBPF(ts_struct_ty, "ts_struct"); b_.CreateStore(b_.getInt64(0), b_.CreateGEP(ts_struct_ty, ts_struct, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore(b_.getInt64(0), b_.CreateGEP(ts_struct_ty, ts_struct, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateStore(epoch, b_.CreateGEP(ts_struct_ty, ts_struct, { b_.getInt64(0), b_.getInt32(2) })); b_.CreateStore(ts_struct, ts_struct_ptr); b_.CreateStore(b_.getInt8(0), key_exists); b_.CreateBr(maybe_clear_block); // Step 3) If we do aggregation, check if we need to reset the bucket before // updating it. b_.SetInsertPoint(maybe_clear_block); Value *ptr = b_.CreateLoad(PointerType::get(llvm_ctx_, 0), ts_struct_ptr); Value *value_ptr = b_.CreateGEP(ts_struct_ty, ptr, { b_.getInt64(0), b_.getInt32(0) }); Value *meta_ptr = b_.CreateGEP(ts_struct_ty, ptr, { b_.getInt64(0), b_.getInt32(1) }); Value *epoch_ptr = b_.CreateGEP(ts_struct_ty, ptr, { b_.getInt64(0), b_.getInt32(2) }); if (tseries_args.agg != TSeriesAggFunc::none) { Value *old_epoch = b_.CreateLoad(b_.getInt64Ty(), epoch_ptr); BasicBlock *clear_block = BasicBlock::Create(module_->getContext(), "clear", parent); b_.CreateCondBr(b_.CreateICmpNE(old_epoch, epoch, "new_epoch"), clear_block, merge_block); b_.SetInsertPoint(clear_block); // Clear the current bucket's value and metadata if it's a new epoch. b_.CreateStore(b_.getInt64(0), value_ptr); b_.CreateStore(b_.getInt64(0), meta_ptr); } // Step 4) Update the current bucket b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); auto &value_arg = call.vargs.at(2); ScopedExpr scoped_expr = visit(value_arg); // promote int to 64-bit Value *cast = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), value_arg.type().IsSigned()); // Update the value and metadata. switch (tseries_args.agg) { case TSeriesAggFunc::avg: b_.CreateStore(b_.CreateAdd(b_.CreateLoad(b_.getInt64Ty(), meta_ptr), b_.getInt64(1)), meta_ptr); [[fallthrough]]; case TSeriesAggFunc::sum: b_.CreateStore(b_.CreateAdd(b_.CreateLoad(b_.getInt64Ty(), value_ptr), cast), value_ptr); break; case TSeriesAggFunc::max: case TSeriesAggFunc::min: b_.CreateMinMax(cast, value_ptr, meta_ptr, tseries_args.agg == TSeriesAggFunc::max, value_arg.type().IsSigned()); break; case TSeriesAggFunc::none: b_.CreateStore(cast, value_ptr); b_.CreateStore(now, meta_ptr); break; default: LOG(BUG) << "disallowed type \"" << tseries_args.agg << "\""; } b_.CreateStore(epoch, epoch_ptr); b_.CreateCondBr(b_.CreateICmpNE(b_.CreateLoad(b_.getInt8Ty(), key_exists), b_.getInt8(1), "needs_update"), update_block, exit_block); b_.SetInsertPoint(update_block); b_.CreateMapUpdateElem(map.ident, scoped_key.value(), ptr, call.loc); b_.CreateBr(exit_block); b_.SetInsertPoint(exit_block); b_.CreateLifetimeEnd(ts_struct); b_.CreateLifetimeEnd(ts_struct_ptr); b_.CreateLifetimeEnd(key_exists); return ScopedExpr(); } else if (call.func == "delete") { auto &map = *call.vargs.at(0).as(); auto scoped_key = getMapKey(map, call.vargs.at(1)); if (!is_bpf_map_clearable(map_types_[map.ident])) { // store zero instead of calling bpf_map_delete_elem() auto *val = b_.CreateWriteMapValueAllocation(map.value_type, map.ident + "_zero", call.loc); b_.CreateStore(Constant::getNullValue(b_.GetType(map.value_type)), val); b_.CreateMapUpdateElem(map.ident, scoped_key.value(), val, call.loc); // Assume delete for these kinds of maps always succeeds. Value *expr = b_.getInt8(1); return ScopedExpr(expr); } else { Value *ret = b_.CreateMapDeleteElem( map, scoped_key.value(), call.ret_val_discarded, call.loc); Value *expr = b_.CreateICmpEQ(ret, b_.getInt64(0), "delete_ret"); return ScopedExpr(expr); } } else if (call.func == "has_key") { auto &arg = call.vargs.at(0); auto &map = *arg.as(); 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") { const auto max_strlen = bpftrace_.config_->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"); } // Note that the successful copying of the string will always include the // NULL byte, so we explicitly poison the string value up front. This // allows the conversion to know when the string has been truncated. We // have added an extra byte to the kernel copy to account for this. // Anything copied out of this will be copied as a str[N] type that may // omit the NUL byte (which indicates that it has been truncated). uint64_t padding = 0; Value *readlen = strlen; if (max_strlen < 1024) { padding = 1; readlen = b_.CreateAdd(readlen, b_.getInt64(padding)); } Value *buf = b_.CreateGetStrAllocation("str", call.loc, padding); b_.CreateMemsetBPF(buf, b_.getInt8(0xff), max_strlen + padding); auto &arg0 = call.vargs.front(); auto scoped_expr = visit(call.vargs.front()); b_.CreateProbeReadStr( buf, readlen, 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 auto max_strlen = bpftrace_.config_->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 = arg.as(); if (literal_length) fixed_buffer_length = literal_length->value; } 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(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); const auto max_size = bpftrace_.config_->max_strlen; b_.CreateMemsetBPF(buf, b_.getInt8(0), max_size); 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(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 = call.vargs.at(0).as()->value; addr = bpftrace_.resolve_kname(name); if (!addr) call.addError() << "Failed to resolve kernel symbol: " << name; return ScopedExpr(b_.getInt64(addr)); } else if (call.func == "percpu_kaddr") { auto name = call.vargs.at(0).as()->value; 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 = call.vargs.at(0).as()->value; struct symbol sym = {}; int err = bpftrace_.resolve_uname(name, &sym, current_attach_point_->target); if (err < 0 || sym.address == 0) call.addError() << "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 = call.vargs.at(0).as()->value; cgroupid = util::resolve_cgroupid(path); return ScopedExpr(b_.getInt64(cgroupid)); } else if (call.func == "join") { createJoinCall(call, async_ids_.join()); 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(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; size_t inet_index = 0; if (call.vargs.size() == 1) { auto &inet = call.vargs.at(0); if (inet.type().IsIntegerTy() || inet.type().GetSize() == 4) { af_type = b_.getInt64(AF_INET); } else { af_type = b_.getInt64(AF_INET6); } } else { inet_index = 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 &inet = call.vargs.at(inet_index); auto scoped_inet = visit(inet); if (inet.type().IsArrayTy() || inet.type().IsStringTy()) { b_.CreateProbeRead(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 = call.vargs.at(0).as()->value; 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) { call.addError() << "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 = call.vargs.at(0).as()->value; auto offset = arch::Host::register_to_pt_regs_offset(reg_name); if (!offset) { call.addError() << "register " << reg_name << " not available on architecture " << arch::Host::Machine; return ScopedExpr(b_.getInt64(0)); } return ScopedExpr(b_.CreateRegisterRead(ctx_, offset.value(), 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); const auto &s = bpftrace_.resources.bpf_print_fmts.at(print_id).str(); // and finally the seq_printf call b_.CreateSeqPrintf(ctx_, b_.CreateIntToPtr(fmt, b_.getPtrTy()), b_.getInt32(s.size() + 1), data, b_.getInt32(data_size), call.loc); return ScopedExpr(); } else { auto async_id = async_ids_.printf(); createFormatStringCall(call, async_id, std::get<1>( bpftrace_.resources.printf_args[async_id]), "printf", async_action::AsyncAction::printf); return ScopedExpr(); } } else if (call.func == "errorf") { auto async_id = async_ids_.printf(); createFormatStringCall(call, async_id, std::get<1>( bpftrace_.resources.printf_args[async_id]), "errorf", async_action::AsyncAction::printf); return ScopedExpr(); } else if (call.func == "debugf") { auto print_id = async_ids_.bpf_print(); auto *fmt = createFmtString(print_id); const auto &s = bpftrace_.resources.bpf_print_fmts.at(print_id).str(); 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(s.size() + 1), values, call.loc); return ScopedExpr(); } else if (call.func == "system") { auto async_id = async_ids_.system(); createFormatStringCall(call, async_id, std::get<1>( bpftrace_.resources.system_args[async_id]), "system", async_action::AsyncAction::syscall); return ScopedExpr(); } else if (call.func == "cat") { auto async_id = async_ids_.cat(); createFormatStringCall(call, async_id, std::get<1>(bpftrace_.resources.cat_args[async_id]), "cat", async_action::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(static_cast(async_action::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(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") { if (call.vargs.at(0).is()) { 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 = *arg.as(); 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(static_cast( async_action::AsyncAction::clear), elements.at(0)), aa_ptr); else b_.CreateStore(b_.GetIntSameSize(static_cast( async_action::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(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 = *arg.as(); // This is defined only for non-scalar maps. if (bpftrace_.feature_->has_kernel_func(Kfunc::bpf_map_sum_elem_count)) { return ScopedExpr(CreateKernelFuncCall(Kfunc::bpf_map_sum_elem_count, { b_.GetMapVar(map.ident) }, "len", call)); } else { if (!map_len_func_) map_len_func_ = createMapLenCallback(); return ScopedExpr( b_.CreateForEachMapElem(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(static_cast(async_action::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(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>( call.return_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.return_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 = arg.as()->value; 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(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(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 = call.vargs.at(2).as()->value; uint64_t size = std::min( { size_opt, left_arg.type().GetSize(), 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.return_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(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( static_cast(async_action::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(buf, struct_size, call.loc); return ScopedExpr(buf, [this, buf] { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "bswap") { auto &arg = call.vargs.at(0); auto scoped_arg = visit(arg); 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(static_cast( async_action::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.return_type.ts_mode == TimestampMode::sw_tai) { return ScopedExpr(createGetNsSwTAI(call.loc)); } else { return ScopedExpr(b_.CreateGetNs(call.return_type.ts_mode, call.loc)); } } else if (call.func == "pid") { bool force_init = shouldForceInitPidNs(call.vargs); return ScopedExpr(b_.CreateGetPid(call.loc, force_init)); } else if (call.func == "tid") { bool force_init = shouldForceInitPidNs(call.vargs); return ScopedExpr(b_.CreateGetTid(call.loc, force_init)); } else if (call.func == "socket_cookie") { auto scoped_arg = visit(call.vargs.at(0)); return ScopedExpr(b_.CreateGetSocketCookie(scoped_arg.value(), call.loc)); } else { auto *func = extern_funcs_[call.func]; if (!func) { // If we don't know about this function for codegen, then it is very // likely something that will be linked in from the standard library. // Assume that the semantic analyser has provided the types correctly, // and set everything up for success. llvm::Type *result_type = b_.GetType(call.return_type); SmallVector arg_types; for (const auto &expr : call.vargs) { arg_types.push_back(b_.GetType(expr.type())); } FunctionType *function_type = FunctionType::get(result_type, arg_types, false); func = llvm::Function::Create(function_type, llvm::Function::ExternalLinkage, call.func, module_.get()); func->addFnAttr(Attribute::AlwaysInline); func->addFnAttr(Attribute::NoUnwind); func->setDSOLocal(true); // Add noundef attribute to each argument. for (auto &arg : func->args()) { arg.addAttr(Attribute::NoUndef); } extern_funcs_[call.func] = func; } std::vector args; SmallVector arg_values; for (auto &expr : call.vargs) { args.emplace_back(visit(expr)); arg_values.push_back(args.back().value()); } auto *inst = b_.CreateCall(func, arg_values, call.func); return ScopedExpr(inst, [this, inst, &call] { // We set the debug location on the call instructions only after the // scoped expression is not longer used. Otherwise the instruction emitter // seems to use this location for everything, which results in problems. inst->setDebugLoc( debug_.createDebugLocation(llvm_ctx_, scope_, call.loc)); }); } } llvm::Value *CodegenLLVM::createGetNsSwTAI(const Location &loc) { if (!bpftrace_.delta_taitime_.has_value()) LOG(BUG) << "delta_taitime_ 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, loc); return b_.CreateAdd(ns, b_.getInt64(delta)); } ScopedExpr CodegenLLVM::visit([[maybe_unused]] Map &map) { // This is not currently used in code generation. Code is generated either // via `MapAccess` for reads or via `AssignMapStatement` for writes. return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(MapAddr &map_addr) { return ScopedExpr(b_.GetMapVar(map_addr.map->ident)); } ScopedExpr CodegenLLVM::visit(Variable &var) { // Arrays and structs are not memcopied for local variables if (needMemcpy(var.var_type) && !(var.var_type.IsArrayTy() || var.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::visit(VariableAddr &var_addr) { return ScopedExpr(getVariable(var_addr.var->ident).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_.CreateIntCast( b_.CreateStrncmp(left_string.value(), right_string.value(), len, inverse), b_.getInt1Ty(), false)); } 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(); const auto &left_array_ty = binop.left.type(); const 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(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_.CreateIntCast( b_.CreateStrncmp(left_string, right_string, len, inverse), b_.getInt1Ty(), false)); } 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 = std::max(binop.left.type().GetSize(), binop.right.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: 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_.CreateCheckedBinop(binop, 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) { // The only other type pointers can be compared to is ints if (!binop.left.type().IsPtrTy()) { rhs = b_.CreatePtrToInt(rhs, b_.GetType(binop.left.type())); } else if (!binop.right.type().IsPtrTy()) { lhs = b_.CreatePtrToInt(lhs, b_.GetType(binop.right.type())); } 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) { bool leftptr = binop.left.type().IsPtrTy(); const auto &ptr_ty = leftptr ? binop.left.type() : binop.right.type(); Value *ptr_expr = leftptr ? lhs : rhs; Value *other_expr = leftptr ? rhs : lhs; return ScopedExpr(b_.CreateGEP(b_.GetType(*ptr_ty.GetPointeeTy()), ptr_expr, binop.op == Operator::PLUS ? other_expr : b_.CreateNeg(other_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); } const 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) { const 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); return ScopedExpr(expr); } 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(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) { const SizedType &type = unop.expr.type(); switch (unop.op) { case Operator::MUL: { ScopedExpr scoped_expr = visit(unop.expr); // FIXME(jordalgo): This requires more investigating/fixing as there still // might be some internal types that don't deref properly after their // address is taken via the & operator, e.g., &$x if (unop.result_type.IsIntegerTy() || unop.result_type.IsPtrTy() || unop.result_type.IsUsernameTy() || unop.result_type.IsTimestampTy() || unop.result_type.IsKsymTy()) { const auto *et = type.GetPointeeTy(); AllocaInst *dst = b_.CreateAllocaBPF(*et, "deref"); b_.CreateProbeRead( 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) { const SizedType &type = unop.expr.type(); if (type.IsIntegerTy()) { return unop_int(unop); } else if (type.IsBoolTy()) { assert(unop.op == Operator::LNOT); ScopedExpr scoped_expr = visit(unop.expr); Value *zero_value = Constant::getNullValue(b_.getInt1Ty()); Value *expr = b_.CreateICmpEQ(scoped_expr.value(), zero_value); return ScopedExpr(expr); } 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.result_type.IsStringTy()) { buf = b_.CreateGetStrAllocation("buf", ternary.loc); const auto max_strlen = bpftrace_.config_->max_strlen; b_.CreateMemsetBPF(buf, b_.getInt8(0), max_strlen); } else if (!ternary.result_type.IsIntTy() && !ternary.result_type.IsNoneTy()) { buf = b_.CreateAllocaBPF(ternary.result_type); b_.CreateMemsetBPF(buf, b_.getInt8(0), ternary.result_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.result_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.result_type), ternary.result_type.IsSigned()); b_.CreateBr(done); BasicBlock *left_end_block = b_.GetInsertBlock(); b_.SetInsertPoint(right_block); auto scoped_right = visit(ternary.right); auto *right_expr = b_.CreateIntCast(scoped_right.value(), b_.GetType(ternary.result_type), ternary.result_type.IsSigned()); b_.CreateBr(done); BasicBlock *right_end_block = b_.GetInsertBlock(); b_.SetInsertPoint(done); auto *phi = b_.CreatePHI(b_.GetType(ternary.result_type), 2, "result"); phi->addIncoming(left_expr, left_end_block); phi->addIncoming(right_expr, right_end_block); return ScopedExpr(phi); } else if (ternary.result_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.result_type.IsTupleTy()) { createTupleCopy( ternary.left.type(), ternary.result_type, buf, scoped_left.value()); } else if (needMemcpy(ternary.result_type)) { b_.CreateMemcpyBPF(buf, scoped_left.value(), ternary.result_type.GetSize()); } else { b_.CreateStore(scoped_left.value(), buf); } b_.CreateBr(done); b_.SetInsertPoint(right_block); auto scoped_right = visit(ternary.right); if (ternary.result_type.IsTupleTy()) { createTupleCopy( ternary.right.type(), ternary.result_type, buf, scoped_right.value()); } else if (needMemcpy(ternary.result_type)) { b_.CreateMemcpyBPF(buf, scoped_right.value(), ternary.result_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(); auto scoped_arg = visit(acc.expr); assert(type.IsRecordTy()); bool is_ctx = type.IsCtxAccess(); bool is_tparg = type.is_tparg; bool is_internal = type.is_internal; bool is_funcarg = type.is_funcarg; if (type.is_funcarg) { auto probe_type = probetype(current_attach_point_->provider); if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit || probe_type == ProbeType::rawtracepoint) return ScopedExpr(b_.CreateKFuncArg(ctx_, acc.field_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.field_type.funcarg_idx), args_type, acc.field_type); } } std::string cast_type = is_tparg ? TracepointFormatParser::get_struct_name( *current_attach_point_) : 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; // Restore the addrspace info // struct MyStruct { const int* a; }; $s = (struct MyStruct *)arg0; $s->a type.SetAS(addrspace); const 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 /*volatile*/); } else { // Since `src` is treated as a offset for a constructed probe read, // we are not constrained in the same way. Value *src = b_.CreateSafeGEP(b_.GetType(type), scoped_arg.value(), { b_.getInt64(0), 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(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_.getInt8Ty(), 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) { // Only allow direct reads if the element is also marked as a BTF type; this // is specifically because the semantic analyzer has marked these cases to // avoid copying through two pointers. SizedType type = arr.expr.type(); // 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 (type.IsArrayTy()) { llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *is_oob = BasicBlock::Create(module_->getContext(), "is_oob", parent); BasicBlock *merge = BasicBlock::Create(module_->getContext(), "oob_merge", parent); Value *cond = b_.CreateICmpUGT( b_.CreateIntCast(scoped_index.value(), b_.getInt64Ty(), false), b_.getInt64(type.GetNumElements() - 1), "oob_cond"); b_.CreateCondBr(cond, is_oob, merge); b_.SetInsertPoint(is_oob); b_.CreateRuntimeError(RuntimeErrorId::ARRAY_ACCESS_OOB, arr.loc); b_.CreateBr(merge); b_.SetInsertPoint(merge); } if (inBpfMemory(arr.element_type) && !type.IsPtrTy()) return readDatastructElemFromStack( std::move(scoped_expr), scoped_index.value(), type, arr.element_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(arr.element_type, index); return probereadDatastructElem(std::move(scoped_expr), offset, type, arr.element_type, arr.loc, "array_access"); } } ScopedExpr CodegenLLVM::visit(TupleAccess &acc) { const SizedType &type = acc.expr.type(); auto scoped_arg = visit(acc.expr); assert(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)); } } ScopedExpr CodegenLLVM::visit(MapAccess &acc) { if (named_param_defaults_.defaults.contains(acc.map->ident)) { if (acc.map->value_type.IsStringTy()) { const auto max_strlen = bpftrace_.config_->max_strlen; Value *np_alloc = b_.CreateGetStrAllocation(acc.map->ident, acc.loc); b_.CreateMemsetBPF(np_alloc, b_.getInt8(0), max_strlen); auto sized_type = bpftrace_.resources.global_vars.get_sized_type( acc.map->ident, bpftrace_.resources, *bpftrace_.config_); b_.CreateMemcpyBPF(np_alloc, module_->getGlobalVariable(acc.map->ident), sized_type.GetSize()); return ScopedExpr(np_alloc, [this, np_alloc]() { b_.CreateLifetimeEnd(np_alloc); }); } return ScopedExpr(b_.CreateLoad(acc.map->value_type.IsBoolTy() ? b_.getInt8Ty() : b_.getInt64Ty(), module_->getGlobalVariable(acc.map->ident), acc.map->ident)); } auto scoped_key = getMapKey(*acc.map, acc.key); auto map_info = bpftrace_.resources.maps_info.find(acc.map->ident); if (map_info == bpftrace_.resources.maps_info.end()) { LOG(BUG) << "map name: \"" << acc.map->ident << "\" not found"; } const auto &val_type = map_info->second.value_type; Value *value; if (canAggPerCpuMapElems(map_info->second.bpf_type, val_type)) { value = b_.CreatePerCpuMapAggElems( *acc.map, scoped_key.value(), val_type, acc.loc); } else { value = b_.CreateMapLookupElem(*acc.map, scoped_key.value(), acc.loc); } return ScopedExpr(value, [this, value] { if (dyn_cast(value)) b_.CreateLifetimeEnd(value); }); } ScopedExpr CodegenLLVM::visit(Cast &cast) { auto scoped_expr = visit(cast.expr); if (cast.cast_type.IsIntTy()) { auto *int_ty = b_.GetType(cast.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()) { // 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.cast_type); b_.CreateProbeRead( buf, cast.cast_type, array, cast.loc, cast.expr.type().GetAS()); array = buf; } return ScopedExpr(b_.CreateLoad(int_ty, array, true /*volatile*/)); } else if (cast.expr.type().IsPtrTy()) { return ScopedExpr(b_.CreatePtrToInt(scoped_expr.value(), int_ty)); } else { return ScopedExpr(b_.CreateIntCast( scoped_expr.value(), b_.getIntNTy(cast.cast_type.GetIntBitWidth()), cast.expr.type().IsBoolTy() ? false : cast.cast_type.IsSigned(), "cast")); } } else if (cast.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 if (cast.cast_type.IsBoolTy()) { if (cast.expr.type().IsStringTy()) { auto *first_char = b_.CreateGEP(b_.getInt8Ty(), scoped_expr.value(), { b_.getInt32(0) }); Value *cond = b_.CreateICmpNE(b_.CreateLoad(b_.getInt8Ty(), first_char), b_.getInt8(0), "bool_cast"); return ScopedExpr(cond); } Value *zero_value = Constant::getNullValue(scoped_expr.value()->getType()); Value *cond = b_.CreateICmpNE(scoped_expr.value(), zero_value, "bool_cast"); return ScopedExpr(cond); } else if (cast.cast_type.IsPtrTy()) { if (cast.expr.type().IsIntTy()) { Value *val = b_.CreateIntToPtr(scoped_expr.value(), b_.getPtrTy()); return ScopedExpr(val); } return scoped_expr; } 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; } const 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(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.tuple_type); compareStructure(tuple.tuple_type, tuple_ty); std::vector> vals; std::vector scoped_exprs; vals.reserve(tuple.elems.size()); for (auto &elem : tuple.elems) { auto scoped_expr = visit(elem); vals.emplace_back(scoped_expr.value(), tuple.loc); scoped_exprs.emplace_back(std::move(scoped_expr)); } auto *buf = createTuple(tuple.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) { auto scoped_expr = visit(assignment.expr); auto scoped_key = getMapKey(*assignment.map, assignment.key); Value *expr = scoped_expr.value(); const auto &map_type = assignment.map->type(); const auto &expr_type = assignment.expr.type(); const auto self_alloca = needAssignMapStatementAllocation(assignment); Value *value = self_alloca ? b_.CreateWriteMapValueAllocation(map_type, assignment.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( value, map_type, expr, assignment.loc, expr_type.GetAS()); } } else { if (assignment.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( assignment.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()) { const 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{ .value = val, .type = 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.var_type, var.loc); if (var.var_type.IsArrayTy() || var.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.var_type)) { auto *val = getVariable(var.ident).value; const auto &expr_type = assignment.expr.type(); if (!expr_type.IsSameSizeRecursive(var.var_type)) { b_.CreateMemsetBPF(val, b_.getInt8(0), var.var_type.GetSize()); if (var.var_type.IsTupleTy()) { createTupleCopy(expr_type, var.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.var_type.IsNoneTy()) { // unused and has no type return ScopedExpr(); } maybeAllocVariable(var.ident, var.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) { auto n = unroll.expr.as()->value; for (uint64_t i = 0; i < n; 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 != n - 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_.emplace_back(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) { scope_stack_.push_back(&f); std::visit([&](auto *iter) { visit(f, *iter); }, f.iterable.value); 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); auto *cmp_value = b_.CreateICmpEQ(scoped_expr.value(), Constant::getNullValue( b_.GetType(pred.expr.type())), "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(Block &block) { scope_stack_.push_back(&block); visit(block.stmts); scope_stack_.pop_back(); return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(BlockExpr &block_expr) { scope_stack_.push_back(&block_expr); visit(block_expr.stmts); ScopedExpr value = visit(block_expr.expr); scope_stack_.pop_back(); return value; } void CodegenLLVM::generateProbe(Probe &probe, const std::string &name, FunctionType *func_type, std::optional usdt_location_index, bool dummy) { auto probe_type = probetype(current_attach_point_->provider); int index = current_attach_point_->index() ?: probe.index(); auto func_name = util::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(util::get_section_name(func_name)); func->addFnAttr(Attribute::NoUnwind); scope_ = 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.empty()) { auto ok = generateWatchpointSetupProbe( func_type, name, current_attach_point_->address, index, probe.loc); if (!ok) { probe.addError() << "unable to setup watchpoint: " << ok.takeError(); } } } void CodegenLLVM::add_probe(AttachPoint &ap, Probe &probe, FunctionType *func_type) { current_attach_point_ = ≈ probefull_ = ap.name(); if (probetype(ap.provider) == ProbeType::usdt) { auto usdt = usdt_helper_.find(bpftrace_.pid(), ap.target, ap.ns, ap.func); if (!usdt.has_value()) { ap.addError() << "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(); generateProbe(probe, probefull_, func_type, i); bpftrace_.add_probe(ap, probe, expansions_.get_expansion(ap), expansions_.get_expanded_funcs(ap), i); current_usdt_location_index_++; } } else { generateProbe(probe, probefull_, func_type); bpftrace_.add_probe(ap, probe, expansions_.get_expansion(ap), expansions_.get_expanded_funcs(ap)); } 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::ranges::transform(subprog.args, 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, false); 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{ .value = alloca, .type = 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::benchmark: 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 (attach_point->index() == 0) attach_point->set_index(getNextIndexForProbe()); add_probe(*attach_point, probe, func_type); generated = true; } if (!generated) { generateProbe(probe, "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, Expression &key_expr) { const auto alloca_created_here = needMapKeyAllocation(map, key_expr); auto scoped_key_expr = visit(key_expr); const auto &key_type = map.key_type; // Allocation needs to be done after recursing via visit(key_expr) so that we // have the expression SSA value. Value *key = alloca_created_here ? b_.CreateMapKeyAllocation(key_type, map.ident + "_key", map.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_type.IsSigned()), key); } else if (map.key_type.IsBoolTy()) { b_.CreateStore( b_.CreateIntCast(scoped_key_expr.value(), b_.getInt8Ty(), false), key); } else { if (key_expr.type().IsArrayTy() || key_expr.type().IsRecordTy()) { // We need to read the entire array/struct and save it b_.CreateProbeRead( key, key_expr.type(), scoped_key_expr.value(), map.loc); } else if (key_expr.type().IsPtrTy()) { b_.CreateStore(scoped_key_expr.value(), key); } 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)); } ScopedExpr CodegenLLVM::getMultiMapKey(Map &map, Expression &key_expr, const std::vector &extra_keys, const Location &loc) { auto scoped_expr = visit(key_expr); 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 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 = key_expr.type().GetSize(); if (inBpfMemory(key_expr.type())) { if (!key_expr.type().IsSameSizeRecursive(map.key_type)) { b_.CreateMemsetBPF(offset_val, b_.getInt8(0), map_key_size); if (key_expr.type().IsTupleTy()) { createTupleCopy( key_expr.type(), map.key_type, offset_val, scoped_expr.value()); } else if (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 (key_expr.type().IsArrayTy() || key_expr.type().IsRecordTy()) { // Read the array/struct into the key b_.CreateProbeRead( offset_val, key_expr.type(), scoped_expr.value(), map.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(), 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::createLogicalAnd(Binop &binop) { assert(binop.left.type().IsIntTy() || binop.left.type().IsPtrTy() || binop.left.type().IsBoolTy()); assert(binop.right.type().IsIntTy() || binop.right.type().IsPtrTy() || binop.right.type().IsBoolTy()); 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_.getInt1Ty(), "&&_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_.getInt1(true), result); b_.CreateBr(merge_block); b_.SetInsertPoint(false_block); b_.CreateStore(b_.getInt1(false), result); b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); return ScopedExpr(b_.CreateLoad(b_.getInt1Ty(), result)); } ScopedExpr CodegenLLVM::createLogicalOr(Binop &binop) { assert(binop.left.type().IsIntTy() || binop.left.type().IsPtrTy() || binop.left.type().IsBoolTy()); assert(binop.right.type().IsIntTy() || binop.right.type().IsPtrTy() || binop.right.type().IsBoolTy()); 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_.getInt1Ty(), "||_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_.getInt1(false), result); b_.CreateBr(merge_block); b_.SetInsertPoint(true_block); b_.CreateStore(b_.getInt1(true), result); b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); return ScopedExpr(b_.CreateLoad(b_.getInt1Ty(), 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"); log2_func->addFnAttr(Attribute::NoUnwind); 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"); linear_func->addFnAttr(Attribute::NoUnwind); 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 MDNode *unroll_disable = MDNode::get( llvm_ctx_, MDString::get(llvm_ctx_, "llvm.loop.unroll.disable")); MDNode *loopid = MDNode::getDistinct(llvm_ctx_, { unroll_disable, unroll_disable }); loopid->replaceOperandWith(0, loopid); return loopid; } void CodegenLLVM::createFormatStringCall(Call &call, int id, const std::vector &call_args, const std::string &call_name, async_action::AsyncAction async_action) { std::vector elements; for (const Field &arg : call_args) { llvm::Type *ty = b_.GetType(arg.type); elements.push_back(ty); } // 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 ringbuf_elems = { b_.getInt64Ty() }; StructType *fmt_struct = nullptr; if (!elements.empty()) { fmt_struct = StructType::create(elements, call_name + "_args_t", false); ringbuf_elems.push_back(fmt_struct); } StructType *ringbuf_struct = StructType::create(ringbuf_elems, call_name + "_t", false); int struct_size = datalayout().getTypeAllocSize(ringbuf_struct); Value *fmt_args = b_.CreateGetFmtStringArgsAllocation(ringbuf_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(ringbuf_struct, fmt_args, { b_.getInt32(0), b_.getInt32(0) }); b_.CreateStore(b_.getInt64(id + static_cast(async_action)), id_offset); Value *fmt_offset = nullptr; if (fmt_struct) { fmt_offset = b_.CreateGEP(ringbuf_struct, fmt_args, { b_.getInt32(0), b_.getInt32(1) }); } 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_offset, { b_.getInt32(0), b_.getInt32(i - 1) }); if (needMemcpy(arg.type())) b_.CreateMemcpyBPF(offset, scoped_arg.value(), arg.type().GetSize()); else b_.CreateStore(scoped_arg.value(), offset); } b_.CreateOutput(fmt_args, struct_size, call.loc); if (dyn_cast(fmt_args)) b_.CreateLifetimeEnd(fmt_args); } Result<> CodegenLLVM::generateWatchpointSetupProbe( FunctionType *func_type, const std::string &expanded_probe_name, int arg_num, int index, const Location &loc) { const auto &arguments = arch::Host::arguments(); if (static_cast(arg_num) >= arguments.size()) { return make_error("argument out of range"); } auto offset = arch::Host::register_to_pt_regs_offset(arguments[arg_num]); if (!offset) { return make_error("invalid register"); } auto func_name = util::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(util::get_section_name(func_name)); func->addFnAttr(Attribute::NoUnwind); 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(b_.getInt32(SIGSTOP), current_attach_point_->loc); // Pull out function argument Value *ctx = func->arg_begin(); Value *addr = b_.CreateRegisterRead(ctx, offset.value(), "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( static_cast(async_action::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(buf, struct_size, loc); b_.CreateLifetimeEnd(buf); createRet(); return OK(); } 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 = *arg.as(); AllocaInst *buf = b_.CreateAllocaBPF(print_struct, call.func + "_" + map.ident); // store asyncactionid: b_.CreateStore( b_.getInt64(static_cast(async_action::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(buf, getStructSize(print_struct), call.loc); b_.CreateLifetimeEnd(buf); } void CodegenLLVM::createJoinCall(Call &call, int id) { 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); uint32_t content_size = bpftrace_.join_argnum_ * bpftrace_.join_argsize_; auto elements = AsyncEvent::Join().asLLVMType(b_, content_size); StructType *join_struct = b_.GetStructType("join_t", elements, true); Value *join_data = b_.CreateBitCast(perfdata, PointerType::get(llvm_ctx_, 0)); b_.CreateStore( b_.getInt64(static_cast(async_action::AsyncAction::join)), b_.CreateGEP(join_struct, join_data, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore( b_.getInt64(id), b_.CreateGEP(join_struct, join_data, { b_.getInt64(0), b_.getInt32(1) })); Value *content_ptr = b_.CreateGEP(join_struct, join_data, { b_.getInt64(0), b_.getInt32(2) }); SizedType elem_type = CreatePointer(CreateInt8(), addrspace); Value *value = scoped_arg.value(); AllocaInst *arr = b_.CreateAllocaBPF(b_.getInt64Ty(), call.func + "_r0"); for (unsigned int i = 0; i < bpftrace_.join_argnum_; i++) { if (i > 0) { value = b_.CreateGEP(b_.GetType(elem_type), value, b_.getInt32(i)); } b_.CreateProbeRead(arr, elem_type, value, call.loc); Value *str_offset = b_.getInt64( static_cast(i) * static_cast(bpftrace_.join_argsize_)); Value *str_ptr = b_.CreateGEP(b_.getInt8Ty(), content_ptr, str_offset); b_.CreateProbeReadStr(str_ptr, bpftrace_.join_argsize_, b_.CreateLoad(b_.getInt64Ty(), arr), addrspace, call.loc); } size_t header_size = offsetof(AsyncEvent::Join, content); // action_id + // join_id size_t total_size = header_size + content_size; b_.CreateOutput(perfdata, total_size, call.loc); b_.CreateBr(failure_callback); b_.SetInsertPoint(failure_callback); } 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( static_cast(async_action::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(content_offset, arg.type(), value, call.loc); } else { b_.CreateStore(value, content_offset); } b_.CreateOutput(buf, struct_size, call.loc); if (dyn_cast(buf)) b_.CreateLifetimeEnd(buf); } 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_internal_repr_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); } // 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; createMapDefinition( name, info.bpf_type, info.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(16, 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, CreateUInt32(), CreateArray(max_stack_limit, CreateUInt64())); } if (codegen_resources.needs_join_map) { auto value_size = offsetof(AsyncEvent::Join, content) + (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, CreateUInt32(), value_type); } if (codegen_resources.needs_elapsed_map) { createMapDefinition(to_string(MapType::Elapsed), libbpf::BPF_MAP_TYPE_HASH, 1, CreateUInt64(), CreateUInt64()); } if (bpftrace_.need_recursion_check_) { createMapDefinition(to_string(MapType::RecursionPrevention), libbpf::BPF_MAP_TYPE_PERCPU_ARRAY, 1, CreateUInt32(), CreateUInt64()); } if (required_resources.using_skboutput) { createMapDefinition(to_string(MapType::PerfEvent), libbpf::BPF_MAP_TYPE_PERF_EVENT_ARRAY, util::get_online_cpus().size(), CreateInt32(), CreateInt32()); } auto entries = bpftrace_.config_->perf_rb_pages * 4096; createMapDefinition(to_string(MapType::Ringbuf), libbpf::BPF_MAP_TYPE_RINGBUF, entries, CreateNone(), CreateNone()); } void CodegenLLVM::generate_global_vars( const RequiredResources &resources, const ::bpftrace::Config &bpftrace_config) { for (const auto &[name, config] : resources.global_vars.global_var_map()) { auto sized_type = resources.global_vars.get_sized_type(name, resources, bpftrace_config); auto *var = llvm::dyn_cast( module_->getOrInsertGlobal(name, b_.GetType(sized_type))); var->setInitializer(Constant::getNullValue(b_.GetType(sized_type))); var->setConstant(config.section == globalvars::RO_SECTION_NAME); var->setSection(config.section); var->setExternallyInitialized(true); var->setDSOLocal(true); var->addDebugInfo(debug_.createGlobalVariable(name, sized_type)); } } // 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() || elem_type.IsBoolTy()) { // Load the correct type from src return ScopedExpr(b_.CreateDatastructElemLoad(elem_type, src)); } 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, const 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_internal) { if (src->getType()->isIntegerTy()) src = b_.CreateIntToPtr(src, dst->getType()); b_.CreateMemcpyBPF(dst, src, elem_type.GetSize()); } else { b_.CreateProbeRead(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()) { // Types have already been suitably casted; just do the access. Value *expr = b_.CreateDatastructElemLoad(elem_type, src); // 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 *cmp = b_.CreateICmpEQ( expr, Constant::getNullValue(b_.GetType(elem_type)), "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(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; const SizedType &type = unop.expr.type(); uint64_t step = type.IsPtrTy() ? type.GetPointeeTy()->GetSize() : 1; if (auto *acc = unop.expr.as()) { auto &map = *acc->map; auto scoped_key = getMapKey(map, acc->key); Value *oldval = b_.CreateMapLookupElem(map, scoped_key.value(), unop.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.value_type, map.ident + "_newval"); if (type.IsPtrTy()) { b_.CreateStore(b_.CreateGEP(b_.GetType(*map.value_type.GetPointeeTy()), oldval, is_increment ? b_.getInt32(1) : b_.getInt32(-1)), newval); } else { 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(map.ident, scoped_key.value(), newval, unop.loc); Value *value; if (unop.is_post_op) value = oldval; else value = b_.CreateLoad(b_.GetType(map.value_type), newval); b_.CreateLifetimeEnd(newval); return ScopedExpr(value); } else if (auto *var = unop.expr.as()) { const auto &variable = getVariable(var->ident); Value *oldval = b_.CreateLoad(variable.type, variable.value); Value *newval; if (type.IsPtrTy()) { newval = b_.CreateGEP(b_.GetType(*type.GetPointeeTy()), oldval, is_increment ? b_.getInt32(1) : b_.getInt32(-1)); } else { 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"); callback->addFnAttr(Attribute::NoUnwind); 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"); callback->addFnAttr(Attribute::NoUnwind); 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; } std::pair CodegenLLVM::createForContext( const For &f, std::vector &&extra_fields) { const auto &ctx_fields = f.ctx_type.GetFields(); std::vector ctx_field_types(ctx_fields.size(), b_.getPtrTy()); // Add all the extra fields. std::ranges::move(extra_fields, std::back_inserter(ctx_field_types)); // Pack pointers to variables into context struct for use in the callback. If // there are no fields, then the underlying codegen helper will simply pass // null as the context value. We should not allocate an empty struct. llvm::Type *ctx_t = nullptr; Value *ctx = nullptr; if (!ctx_field_types.empty()) { 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); } } return { ctx_t, ctx }; } llvm::Function *CodegenLLVM::createForCallback( For &f, const std::string &name, ArrayRef args, const Struct &debug_args, llvm::Type *ctx_t, std::function decl) { auto saved_ip = b_.saveIP(); // All callbacks in BPF will be generated with a standard integer return. FunctionType *callback_type = FunctionType::get(b_.getInt64Ty(), args, false); auto *callback = llvm::Function::Create( callback_type, llvm::Function::LinkageTypes::InternalLinkage, name, module_.get()); callback->setDSOLocal(true); callback->setVisibility(llvm::GlobalValue::DefaultVisibility); callback->setSection(".text"); callback->addFnAttr(Attribute::NoUnwind); // Add the debug information. debug_.createFunctionDebugInfo(*callback, CreateInt64(), debug_args); // Start our basic function block. auto *for_body = BasicBlock::Create(module_->getContext(), "for_body", callback); auto *for_continue = BasicBlock::Create(module_->getContext(), "for_continue", callback); auto *for_break = BasicBlock::Create(module_->getContext(), "for_break", callback); b_.SetInsertPoint(for_body); // Extract our context type and value. As noted, this requires that some // member of `debug_args` is named `ctx`. size_t ctx_index = 0; while (ctx_index < debug_args.fields.size()) { if (debug_args.fields[ctx_index].name == "ctx") { break; } ctx_index++; } assert(ctx_index < debug_args.fields.size()); llvm::Value *ctx = callback->getArg(ctx_index); // Generate the variable declaration. variables_[scope_stack_.back()][f.decl->ident] = VariableLLVM{ .value = decl(callback), .type = 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 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. loops_.emplace_back(for_continue, for_break); visit(f.stmts); b_.CreateBr(for_continue); loops_.pop_back(); b_.SetInsertPoint(for_continue); b_.CreateRet(b_.getInt64(0)); b_.SetInsertPoint(for_break); b_.CreateRet(b_.getInt64(1)); // 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; } ScopedExpr CodegenLLVM::visit(For &f, Range &range) { // Evaluate our starting and endpoint values. auto start = visit(range.start); auto end = visit(range.end); Value *iters = b_.CreateBinOp(Instruction::Sub, end.value(), start.value()); // Construct the context and callback with extra fields add to the context, // which track the starting value and the current value of the iteration. auto [ctx_t, ctx] = createForContext(f, { b_.getInt64Ty(), b_.getInt64Ty() }); const auto sz = f.ctx_type.GetFields().size(); b_.CreateStore(start.value(), b_.CreateSafeGEP(ctx_t, ctx, { b_.getInt64(0), b_.getInt32(sz) }, "ctx.start")); // Create a callback function suitable for passing to bpf_loop, for the form: // // static int cb(uint64_t index, void *ctx) // { // $x = index+prefix; // [stmts...] // } std::array args = { b_.getInt64Ty(), b_.getPtrTy() }; Struct debug_args; debug_args.AddField("index", CreateInt64()); debug_args.AddField("ctx", CreatePointer(CreateInt8())); auto *cb = createForCallback( f, "loop_cb", args, debug_args, ctx_t, [&](llvm::Function *callback) { // See above. Prior to the callback, we push the starting value into the // context as an extra field, and reserve space for the current value // there. This must be set on each iteration, and provides a declaration // that points there. auto *ctx = callback->getArg(1); auto *start_field_ptr = b_.CreateSafeGEP( ctx_t, ctx, { b_.getInt64(0), b_.getInt32(sz) }, "start"); auto *current_field_ptr = b_.CreateSafeGEP( ctx_t, ctx, { b_.getInt64(0), b_.getInt32(sz + 1) }, "current"); // Add the current iteration count to our starting count, reseting the // value of the current variable in the context. The starting value is // not available to the user, simply the value of the current iteration. b_.CreateStore(b_.CreateAdd(b_.CreateLoad(b_.getInt64Ty(), start_field_ptr), callback->getArg(0)), current_field_ptr); return current_field_ptr; }); // Execute the loop. b_.CreateForRange(iters, cb, ctx, f.loc); return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(For &f, Map &map) { // Construct our context types. auto [ctx_t, ctx] = createForContext(f); // 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...] // } std::array args = { b_.getPtrTy(), b_.getPtrTy(), b_.getPtrTy(), b_.getPtrTy() }; 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())); const std::string name = "map_for_each_cb"; auto *cb = createForCallback( f, name, args, debug_args, ctx_t, [&](llvm::Function *callback) { 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_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_info->second.bpf_type, map_val_type)) { val = b_.CreatePerCpuMapAggElems( map, callback->getArg(1), map_val_type, f.loc); } else if (!inBpfMemory(val_type)) { val = b_.CreateLoad(b_.GetType(val_type), val, "val"); } return createTuple(f.decl->type(), { { key, f.decl->loc }, { val, f.decl->loc } }, f.decl->ident, f.decl->loc); }); // Invoke via the helper. b_.CreateForEachMapElem(map, cb, ctx, f.loc); return ScopedExpr(); } bool CodegenLLVM::canAggPerCpuMapElems(const libbpf::bpf_map_type map_type, const SizedType &val_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) { const auto &s = bpftrace_.resources.bpf_print_fmts.at(print_id).str(); auto *res = llvm::dyn_cast( module_->getOrInsertGlobal("__fmt_" + std::to_string(print_id), ArrayType::get(b_.getInt8Ty(), s.size() + 1))); res->setConstant(true); res->setInitializer(ConstantDataArray::getString(module_->getContext(), s)); 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, Node &call) { const std::string &func_name = kfunc_name(kfunc); if (auto *fun = module_->getFunction(func_name)) return fun; std::string err; auto func_struct = bpftrace_.btf_->resolve_args( func_name, true, false, false, err); if (!func_struct) { call.addError() << "Unknown kernel function: " << func_name; return nullptr; } Struct debug_args; std::vector args; for (auto &field : func_struct->fields) { if (field.name != RETVAL_FIELD_NAME) { args.push_back(b_.GetType(field.type)); debug_args.AddField(field.name, field.type, field.offset, field.bitfield, field.is_data_loc); } } FunctionType *func_type = FunctionType::get( b_.GetType(func_struct->GetField(RETVAL_FIELD_NAME).type), args, false); auto *fun = llvm::Function::Create(func_type, llvm::GlobalValue::ExternalWeakLinkage, func_name, module_.get()); fun->setSection(".ksyms"); fun->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); fun->addFnAttr(Attribute::NoUnwind); debug_.createFunctionDebugInfo( *fun, func_struct->GetField(RETVAL_FIELD_NAME).type, debug_args, true); return fun; } CallInst *CodegenLLVM::CreateKernelFuncCall(Kfunc kfunc, ArrayRef args, const Twine &name, Node &call) { auto *func = DeclareKernelFunc(kfunc, call); 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; } std::unique_ptr CodegenLLVM::compile() { CodegenResourceAnalyser analyser(*bpftrace_.config_); auto codegen_resources = analyser.analyse(*ast_.root); generate_maps(bpftrace_.resources, codegen_resources); generate_global_vars(bpftrace_.resources, *bpftrace_.config_); { visit(ast_.root); } debug_.finalize(); return std::move(module_); } Pass CreateLLVMInitPass() { LLVMInitializeBPFTargetInfo(); LLVMInitializeBPFTarget(); LLVMInitializeBPFTargetMC(); LLVMInitializeBPFAsmPrinter(); return Pass::create("llvm-init", [] { return CompileContext(); }); } Pass CreateCompilePass( std::optional> &&usdt_helper) { return Pass::create("compile", [usdt_helper](ASTContext &ast, BPFtrace &bpftrace, CDefinitions &c_definitions, NamedParamDefaults &named_param_defaults, CompileContext &ctx, ExpansionResult &expansions) mutable { USDTHelper default_usdt; if (!usdt_helper) { usdt_helper = std::ref(default_usdt); } CodegenLLVM llvm(ast, bpftrace, c_definitions, named_param_defaults, *ctx.context, usdt_helper->get(), expansions); return CompiledModule(llvm.compile()); }); } Pass CreateLinkBitcodePass() { return Pass::create( "LinkBitcode", [](BitcodeModules &bm, CompiledModule &cm) -> Result<> { for (auto &mod : bm.modules) { // Make a copy of the module, to ensure this is not modifying the // original. The link step must consume the module below. auto copy = llvm::CloneModule(*mod); // Modify to ensure everything is inlined. Note that this is also // marking all these functions for below, which will adjust their // linkage. // // We also want to ensure that we remove any attributes that prevent // any subsequent inline (such as "OptimizeNone"), and suitably tag // these functions are "NoUnwind", like the rest. There is no // unwinding in BPF, and these attributes are just ensuring that. for (auto &fn : copy->functions()) { if (fn.isDSOLocal() && !fn.isIntrinsic()) { fn.removeFnAttr(Attribute::NoInline); fn.removeFnAttr(Attribute::OptimizeNone); fn.addFnAttr(Attribute::AlwaysInline); fn.addFnAttr(Attribute::NoUnwind); } } // Link into the original source module, consume the new one. This // function returns `false` on success. Hopefully this path is // unlikely to cause errors, since it seems the information available // is sparse. auto err = Linker::linkModules(*cm.module, std::move(copy), Linker::LinkOnlyNeeded); if (err) { return make_error("error during LLVM linking", EINVAL); } } return OK(); }); } Pass CreateVerifyPass() { return Pass::create("verify", [](CompiledModule &cm) -> Result<> { std::stringstream ss; raw_os_ostream OS(ss); bool ret = llvm::verifyModule(*cm.module, &OS); OS.flush(); if (ret) { return make_error( "LLVM verification failed (--verify-llvm-ir)\n" + ss.str(), 0); } return OK(); }); } Pass CreateOptimizePass() { return Pass::create("optimize", [](CompiledModule &cm) { PipelineTuningOptions pto; pto.LoopUnrolling = false; pto.LoopInterleaving = false; pto.LoopVectorization = false; pto.SLPVectorization = false; llvm::PassBuilder pb(getTargetMachine(), pto); // ModuleAnalysisManager must be destroyed first. llvm::LoopAnalysisManager lam; llvm::FunctionAnalysisManager fam; llvm::CGSCCAnalysisManager cgam; llvm::ModuleAnalysisManager mam; // Register all the basic analyses with the managers. pb.registerModuleAnalyses(mam); pb.registerCGSCCAnalyses(cgam); pb.registerFunctionAnalyses(fam); pb.registerLoopAnalyses(lam); pb.crossRegisterProxies(lam, fam, cgam, mam); ModulePassManager mpm = pb.buildPerModuleDefaultPipeline( llvm::OptimizationLevel::O3); mpm.run(*cm.module, mam); }); } Pass CreateDumpIRPass(std::ostream &out) { return Pass::create("dump-ir", [&out](CompiledModule &cm) { raw_os_ostream os(out); cm.module->print(os, nullptr, false, true); os.flush(); out.flush(); }); } Pass CreateObjectPass() { return Pass::create("object", [](CompiledModule &cm) { SmallVector output; raw_svector_ostream os(output); legacy::PassManager PM; #if LLVM_VERSION_MAJOR >= 18 auto type = CodeGenFileType::ObjectFile; #else auto type = llvm::CGFT_ObjectFile; #endif if (getTargetMachine()->addPassesToEmitFile(PM, os, nullptr, type)) LOG(BUG) << "Cannot emit a file of this type"; PM.run(*cm.module); return BpfObject(output); }); } Pass CreateDumpASMPass([[maybe_unused]] std::ostream &out) { return Pass::create("dump-asm", [](BpfObject &bpf) { // 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. 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(bpf.data.data(), sizeof(char), bpf.data.size(), objdump) != bpf.data.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; } }); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/codegen_llvm.h000066400000000000000000000051121506776124200210600ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "ast/pass_manager.h" #include "ast/passes/link.h" #include "usdt.h" namespace bpftrace::ast { class CompileContext : public ast::State<"compile-context"> { public: CompileContext() : context(std::make_unique()) {}; std::unique_ptr context; }; // LLVMInit will create the required LLVM context which can be subsequently // shared by other passes. This should always be added, unless an external // `LLVMContext` is injected into the pass ahead of time. Pass CreateLLVMInitPass(); class CompiledModule : public ast::State<"compiled-module"> { public: CompiledModule(std::unique_ptr module) : module(std::move(module)) {}; std::unique_ptr module; }; // Compiles the primary AST, and emits `CompiledModule`. Pass CreateCompilePass(std::optional> &&usdt_helper = std::nullopt); // Links any external bitcode into the module. This must follow the compile // pass, and should proceed any verification, optimization or external linking. Pass CreateLinkBitcodePass(); // Dumps `CompiledModule` to the given stream. Pass CreateDumpIRPass(std::ostream &out); // Validates `CompiledModule`, and attaches an error diagnostic to the program // itself if verification fails. Pass CreateVerifyPass(); // In-place optimizes the `CompiledModule` emitted by the compile pass. Pass CreateOptimizePass(); class BpfObject : public ast::State<"bpf-object"> { public: BpfObject(std::span data) : data(data.begin(), data.end()) {}; std::vector data; }; // Produces the ELF data for the BPF bytecode as a `BpfObject`. This is // required by the Link pass below. Pass CreateObjectPass(); // Dumps `BpfObject` as disassembled bytecode. Pass CreateDumpASMPass(std::ostream &out); // AllCompilePasses returns a vector of passes representing all compile passes, // in the expected order. inline std::vector AllCompilePasses( std::optional> &&usdt_helper = std::nullopt) { std::vector passes; passes.emplace_back(CreateCompilePass(std::move(usdt_helper))); passes.emplace_back(CreateLinkBitcodePass()); passes.emplace_back(CreateVerifyPass()); passes.emplace_back(CreateOptimizePass()); passes.emplace_back(CreateObjectPass()); passes.emplace_back(CreateExternObjectPass()); passes.emplace_back(CreateLinkPass()); return passes; } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/codegen_resources.cpp000066400000000000000000000016311506776124200224550ustar00rootroot00000000000000#include "codegen_resources.h" #include "types.h" namespace bpftrace::ast { CodegenResourceAnalyser::CodegenResourceAnalyser( const ::bpftrace::Config &config) : config_(config) { } CodegenResources CodegenResourceAnalyser::analyse(Program &program) { visit(program); return std::move(resources_); } void CodegenResourceAnalyser::visit(Builtin &builtin) { if (builtin.ident == "__builtin_elapsed") { resources_.needs_elapsed_map = true; } else if (builtin.ident == "kstack" || builtin.ident == "ustack") { resources_.stackid_maps.insert(StackType{ .mode = config_.stack_mode }); } } 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.return_type.stack_type); } } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/codegen_resources.h000066400000000000000000000016061506776124200221240ustar00rootroot00000000000000#pragma once #include #include "ast/visitor.h" #include "config.h" namespace bpftrace::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(const ::bpftrace::Config &config); CodegenResources analyse(Program &program); using Visitor::visit; void visit(Builtin &builtin); void visit(Call &call); private: const ::bpftrace::Config &config_; CodegenResources resources_; }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/collect_nodes.h000066400000000000000000000017121506776124200212410ustar00rootroot00000000000000#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() : 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.24.1/src/ast/passes/config_analyser.cpp000066400000000000000000000041061506776124200221220ustar00rootroot00000000000000#include #include #include "ast/ast.h" #include "ast/passes/config_analyser.h" #include "ast/visitor.h" #include "bpftrace.h" #include "config.h" namespace bpftrace::ast { namespace { class ConfigAnalyser : public Visitor { public: explicit ConfigAnalyser(BPFtrace &bpftrace) : bpftrace_(bpftrace) {}; using Visitor::visit; void visit(AssignConfigVarStatement &assignment); private: BPFtrace &bpftrace_; }; } // namespace static std::unordered_set DEPRECATED_CONFIGS = { "symbol_source", "max_type_res_iterations", }; void ConfigAnalyser::visit(AssignConfigVarStatement &assignment) { // If this is deprecated, just emit a warning and move on. This is done here // because they are no longer understood by the actual config type. if (DEPRECATED_CONFIGS.contains(assignment.var)) { assignment.addWarning() << assignment.var << " is deprecated and has no effect"; } std::string var(assignment.var); while (true) { // Set the variable. auto ok = std::visit( [&](const auto &v) { return bpftrace_.config_->set(var, v); }, assignment.value); // Attempt to handle a rename error, and find the new name. This is done // here rather than the config so that we can emit a suitable error. if (!ok) { ok = handleErrors(std::move(ok), [&](const RenameError &e) { var = e.new_name(); }); if (!ok) { assignment.addError() << ok.takeError(); return; } continue; } break; // All set. } if (var != assignment.var) { assignment.addWarning() << assignment.var << " has been renamed, please use " << var; } } Pass CreateConfigPass() { auto fn = [](ASTContext &ast, BPFtrace &b) -> Result { auto configs = ConfigAnalyser(b); configs.visit(ast.root); // Reload any environment changes. auto ok = b.config_->load_environment(); if (!ok) { return ok.takeError(); } return OK(); }; return Pass::create("ConfigAnalyser", fn); }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/config_analyser.h000066400000000000000000000001771506776124200215730ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateConfigPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/deprecated.cpp000066400000000000000000000037651506776124200210710ustar00rootroot00000000000000#include #include "ast/ast.h" #include "ast/passes/deprecated.h" #include "ast/visitor.h" namespace bpftrace::ast { namespace { class DeprecatedAnalyser : public Visitor { public: using Visitor::visit; void visit(Builtin &builtin); void visit(Call &call); }; struct DeprecatedName { std::string old_name; std::string new_name; // True if this name no longer exists - there is no `new_name`. bool deleted; 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; } }; } // namespace static void check(const std::vector &list, const std::string &ident, Node &node) { for (const auto &item : list) { if (!item.matches(ident)) { continue; } if (item.deleted) { auto &err = node.addError(); err << item.old_name << " is deprecated and has no effect."; } else { auto &warn = node.addWarning(); warn << item.old_name << " is deprecated and will be removed in the future."; warn.addHint() << "Use " << item.new_name << " instead."; } } } static std::vector DEPRECATED_BUILTINS = { { .old_name = "sarg*", .new_name = "*(reg(\"sp\") + )", .deleted = false, }, }; void DeprecatedAnalyser::visit(Builtin &builtin) { check(DEPRECATED_BUILTINS, builtin.ident, builtin); } static std::vector DEPRECATED_CALLS = {}; void DeprecatedAnalyser::visit(Call &call) { check(DEPRECATED_CALLS, call.func, call); } Pass CreateDeprecatedPass() { auto fn = [](ASTContext &ast) { DeprecatedAnalyser deprecated; deprecated.visit(ast.root); }; return Pass::create("Deprecated", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/deprecated.h000066400000000000000000000002031506776124200205160ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateDeprecatedPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/field_analyser.cpp000066400000000000000000000257561506776124200217560ustar00rootroot00000000000000#include #include "arch/arch.h" #include "ast/passes/field_analyser.h" #include "ast/passes/probe_expansion.h" #include "ast/visitor.h" #include "bpftrace.h" #include "dwarf_parser.h" #include "probe_matcher.h" #include "util/strings.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace::ast { namespace { class FieldAnalyser : public Visitor { public: explicit FieldAnalyser(BPFtrace &bpftrace, ExpansionResult &expansions) : bpftrace_(bpftrace), expansions_(expansions) { } 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(MapAccess &acc); 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); 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_; ExpansionResult &expansions_; libbpf::bpf_prog_type prog_type_{ libbpf::BPF_PROG_TYPE_UNSPEC }; bool has_builtin_args_; Probe *probe_ = nullptr; std::map var_types_; }; } // namespace 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 == "__builtin_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 == "__builtin_retval") { if (!probe_) return; resolve_args(*probe_); const 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) { 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_) { const 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(MapAccess &acc) { visit(acc.key); visit(acc.map); // Leaves sized_type_ as value type. } void FieldAnalyser::visit(Cast &cast) { visit(cast.expr); resolve_type(cast.cast_type); } void FieldAnalyser::visit(Sizeof &szof) { if (std::holds_alternative(szof.record)) { resolve_type(std::get(szof.record)); } else { visit(szof.record); } } void FieldAnalyser::visit(Offsetof &offof) { if (std::holds_alternative(offof.record)) { resolve_type(std::get(offof.record)); } else { visit(offof.record); } } void FieldAnalyser::visit(AssignMapStatement &assignment) { visit(assignment.map); visit(assignment.key); 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" std::shared_ptr probe_args; auto probe_type = probetype(ap->provider); if (probe_type != ProbeType::fentry && probe_type != ProbeType::fexit && probe_type != ProbeType::rawtracepoint && probe_type != ProbeType::uprobe) continue; if (expansions_.get_expansion(*ap) != 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) { probe.addError() << e.what(); return; } // ... and check if they share same arguments. std::shared_ptr ap_args; for (const auto &match : matches) { // Both uprobes and fentry have a target (binary for uprobes, kernel // module for fentry). std::string func = match; std::string target = util::erase_prefix(func); std::string err; // 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) { ap_args = bpftrace_.btf_->resolve_args( func, probe_type == ProbeType::fexit, true, false, err); } else if (probe_type == ProbeType::rawtracepoint) { ap_args = bpftrace_.btf_->resolve_raw_tracepoint_args(func, err); } else { // uprobe Dwarf *dwarf = bpftrace_.get_dwarf(target); if (dwarf) ap_args = dwarf->resolve_args(func); else ap->addWarning() << "No debuginfo found for " << target; } if (!ap_args) { ap->addWarning() << probetypeName(probe_type) << ap->func << ": " << err; continue; } if (!probe_args) probe_args = ap_args; else if (*ap_args != *probe_args) { ap->addError() << "Probe has attach points with mixed arguments"; break; } } } else { std::string err; // Resolving args for an explicit function failed, print an error and fail if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit) { probe_args = bpftrace_.btf_->resolve_args( ap->func, probe_type == ProbeType::fexit, true, false, err); } else if (probe_type == ProbeType::rawtracepoint) { probe_args = bpftrace_.btf_->resolve_raw_tracepoint_args(ap->func, err); } else { // uprobe Dwarf *dwarf = bpftrace_.get_dwarf(ap->target); if (dwarf) { probe_args = dwarf->resolve_args(ap->func); } else { ap->addWarning() << "No debuginfo found for " << ap->target; } if (probe_args && probe_args->fields.size() >= arch::Host::arguments().size()) { ap->addError() << "\'args\' builtin is not supported for " << "probes with stack-passed arguments."; } } if (!probe_args) { ap->addError() << probetypeName(probe_type) << ap->func << ": " << err; return; } } // 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 ap->addError() << "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)); } } } 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; const 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); } Pass CreateFieldAnalyserPass() { auto fn = [](ASTContext &ast, BPFtrace &b, ExpansionResult &expansions) { FieldAnalyser analyser(b, expansions); analyser.visit(ast.root); }; return Pass::create("FieldAnalyser", fn); }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/field_analyser.h000066400000000000000000000002061506776124200214020ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateFieldAnalyserPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/fold_literals.cpp000066400000000000000000000515441506776124200216120ustar00rootroot00000000000000#include #include "ast/ast.h" #include "ast/passes/fold_literals.h" #include "ast/visitor.h" #include "bpftrace.h" #include "log.h" #include "util/int_parser.h" namespace bpftrace::ast { namespace { const auto FLOW_ERROR = "unable to fold literals due to overflow or underflow"; class LiteralFolder : public Visitor> { public: LiteralFolder(ASTContext &ast) : ast_(ast) {}; LiteralFolder(ASTContext &ast, BPFtrace &bpftrace) : ast_(ast), bpftrace_(std::ref(bpftrace)) {}; using Visitor>::visit; std::optional visit(Cast &cast); std::optional visit(Unop &op); std::optional visit(Binop &op); std::optional visit(Ternary &op); std::optional visit(PositionalParameterCount ¶m); std::optional visit(PositionalParameter ¶m); std::optional visit(Call &call); std::optional visit(Expression &expr); std::optional visit(Probe &probe); std::optional visit(Builtin &builtin); private: ASTContext &ast_; std::optional> bpftrace_; Node *top_level_node_ = nullptr; }; } // namespace template static Expression make_boolean(ASTContext &ast, T left, T right, Binop &op) { bool value = true; switch (op.op) { case Operator::EQ: value = left == right; break; case Operator::NE: value = left != right; break; case Operator::LE: value = left <= right; break; case Operator::GE: value = left >= right; break; case Operator::LT: value = left < right; break; case Operator::GT: value = left > right; break; case Operator::LAND: value = left && right; break; case Operator::LOR: value = left || right; break; case Operator::PLUS: value = left + right; break; case Operator::MINUS: value = left - right; break; case Operator::MUL: value = left && right; break; case Operator::DIV: { if (!right) { op.addError() << FLOW_ERROR; } value = static_cast(left && right); break; } case Operator::MOD: if (!right) { op.addError() << FLOW_ERROR; } value = false; break; case Operator::BAND: value = left & right; break; case Operator::BOR: value = left | right; break; case Operator::BXOR: value = left ^ right; break; case Operator::LEFT: value = left < right; break; case Operator::RIGHT: value = left > right; break; case Operator::LNOT: case Operator::BNOT: case Operator::INVALID: case Operator::ASSIGN: case Operator::INCREMENT: case Operator::DECREMENT: LOG(BUG) << "binary operator is not valid: " << static_cast(op.op); } return ast.make_node(value, Location(op.loc)); } template static std::optional> eval_binop(T left, T right, Operator op) { auto clamp = [](auto v) -> std::variant { if constexpr (std::is_same_v) { if (v >= 0) { return static_cast(v); } } return v; }; switch (op) { case Operator::PLUS: if (left == 0 || right == 0) { return clamp(left + right); } if (left > 0 && right > 0) { auto res = static_cast(left) + static_cast(right); if (res > static_cast(left) && res > static_cast(right)) { return res; } return std::nullopt; } if constexpr (std::is_same_v) { if ((left > 0 && right < 0) || (left < 0 && right > 0)) { return clamp(left + right); } if (left < 0 && right < 0) { if (std::numeric_limits::min() - left > right) return std::nullopt; return left + right; } } return std::nullopt; case Operator::MINUS: if constexpr (std::is_same_v) { if (left >= right) { return left - right; } else { // Ensure that this is representable during conversion. We know that // right > left, therefore the delta here is guaranteed to be greater // than zero. uint64_t delta = right - left - 1; auto max = static_cast(std::numeric_limits::max()); if (delta > max) { return std::nullopt; } else { return -static_cast(delta) - 1; } } } else { if (right == 0) { return clamp(left); } else { if (left > 0 && right < 0 && std::numeric_limits::max() - left < -right) { return std::nullopt; } else if (left < 0 && right > 0 && std::numeric_limits::min() - left > -right) { return std::nullopt; } return clamp(left - right); } } case Operator::MUL: if (left == 0 || right == 0) { return static_cast(0); } else { if constexpr (std::is_same_v) { // Spill into unsigned. if (left < 0 && right < 0) { return eval_binop(static_cast(-(left + 1)) + 1, static_cast(-(right + 1)) + 1, op); } } auto result = left * right; if (result / left != right) { return std::nullopt; // Overflow. } return clamp(result); } case Operator::DIV: if (right == 0) { return std::nullopt; } return clamp(left / right); case Operator::MOD: if (right == 0) { return std::nullopt; } return clamp(left % right); case Operator::BAND: return clamp(left & right); case Operator::BOR: return clamp(left | right); case Operator::BXOR: return clamp(left ^ right); case Operator::LEFT: // Shifting negative amount of bits or more bits than the width of `left` // (which is always 64 in our case) is undefined behavior in C++ if (right < 0 || right >= 64) return std::nullopt; return clamp(left << right); case Operator::RIGHT: // Same as above if (right < 0 || right >= 64) return std::nullopt; return clamp(left >> right); // Comparison operators are handled in `make_boolean` and checked in // `is_comparison_op`. case Operator::EQ: case Operator::NE: case Operator::LE: case Operator::GE: case Operator::LT: case Operator::GT: case Operator::LAND: case Operator::LOR: case Operator::INVALID: case Operator::ASSIGN: case Operator::INCREMENT: case Operator::DECREMENT: case Operator::LNOT: case Operator::BNOT: break; } LOG(BUG) << "Unexpected binary operator: " << static_cast(op); __builtin_unreachable(); } std::optional LiteralFolder::visit(Cast &cast) { visit(cast.expr); return std::nullopt; } std::optional LiteralFolder::visit(Unop &op) { visit(op.expr); if (auto *integer = op.expr.as()) { bool force_unsigned = !integer->type().IsSigned(); if (op.op == Operator::BNOT) { // Still positive. return ast_.make_node(~integer->value, Location(op.loc), force_unsigned); } else if (op.op == Operator::LNOT) { return ast_.make_node(!integer->value, Location(op.loc)); } else if (op.op == Operator::MINUS) { // Ensure that it is representable as a negative value. if (integer->value > static_cast(std::numeric_limits::max()) + 1) { op.addError() << "negative value will underflow"; return std::nullopt; } // Carefully make the conversion. We need to ensure that this does not // overflow/underflow while doing the math. if (integer->value == 0) { return integer; // Drop the operation. } else if (integer->value <= 1) { return ast_.make_node( -static_cast(integer->value), Location(op.loc)); } else { int64_t value = -1; value -= static_cast(integer->value - 1); return ast_.make_node(value, Location(op.loc)); } } } else if (auto *integer = op.expr.as()) { if (op.op == Operator::BNOT) { // Always positive. return ast_.make_node(static_cast(~integer->value), Location(op.loc)); } else if (op.op == Operator::LNOT) { return ast_.make_node(!integer->value, Location(op.loc)); } else if (op.op == Operator::MINUS) { // Ensure that it doesn't overflow. We do this by ensuring that is // representable as a positive number, casting, and then adding 1 to // the unsigned form. int64_t value = integer->value + 1; value = -value; return ast_.make_node(static_cast(value) + 1, Location(op.loc)); } } else if (auto *boolean = op.expr.as()) { // Just supporting logical not for now but we could support other // operations like BNOT (e.g. ~false == -1) in the future. if (op.op == Operator::LNOT) { return ast_.make_node(!boolean->value, Location(op.loc)); } } return std::nullopt; } std::optional LiteralFolder::visit(Binop &op) { visit(op.left); visit(op.right); // Check for string cases. auto *str = op.left.as(); auto other = op.right; if (str == nullptr) { str = op.right.as(); other = op.left; } if (str) { // For whatever reason you are allowed to fold "foo"+3. auto *integer = other.as(); if (op.op == Operator::PLUS && integer) { if (integer->value >= static_cast(str->value.size())) { op.addWarning() << "literal string will always be empty"; return ast_.make_node("", Location(op.loc)); } return ast_.make_node(str->value.substr(integer->value), Location(op.loc)); } auto *rb = other.as(); if (rb) { if (op.op == Operator::LAND || op.op == Operator::LOR) { return make_boolean(ast_, !str->value.empty(), rb->value, op); } } // Check for another string. auto *rs = other.as(); if (!rs) { // Let's just make sure it's not a negative literal. if (other.is()) { op.addError() << "illegal literal operation with strings"; } // This is a mix of a string and something else. This may be a runtime // type, and we need to leave it up to the semantic analysis. return std::nullopt; } switch (op.op) { case Operator::EQ: return ast_.make_node(str->value == rs->value, Location(op.loc)); case Operator::NE: return ast_.make_node(str->value != rs->value, Location(op.loc)); case Operator::LE: return ast_.make_node(str->value <= rs->value, Location(op.loc)); case Operator::GE: return ast_.make_node(str->value >= rs->value, Location(op.loc)); case Operator::LT: return ast_.make_node(str->value < rs->value, Location(op.loc)); case Operator::GT: return ast_.make_node(str->value > rs->value, Location(op.loc)); case Operator::LAND: return ast_.make_node(!str->value.empty() && rs->value.empty(), Location(op.loc)); case Operator::LOR: return ast_.make_node(!str->value.empty() || rs->value.empty(), Location(op.loc)); case Operator::PLUS: return ast_.make_node(str->value + rs->value, Location(op.loc)); default: // What are they tring to do? op.addError() << "illegal literal operation with strings"; return std::nullopt; } } // Handle boolean cases. auto *boolean = op.left.as(); other = op.right; if (boolean == nullptr) { boolean = op.right.as(); other = op.left; } if (boolean) { auto *other_boolean = other.as(); if (other_boolean) { return make_boolean(ast_, boolean->value, other_boolean->value, op); } auto *ru = other.as(); if (ru) { if (op.op == Operator::LAND || op.op == Operator::LOR) { return make_boolean(ast_, boolean->value, ru->value != 0, op); } } auto *rs = other.as(); if (rs) { if (op.op == Operator::LAND || op.op == Operator::LOR) { // Negatives are always true. return make_boolean(ast_, boolean->value, true, op); } } } // Handle all integer cases. std::optional> result; auto *lu = op.left.as(); auto *ls = op.left.as(); auto *ru = op.right.as(); auto *rs = op.right.as(); bool force_unsigned = false; // Only allow operations when we can safely marshall to two of the same type. // Then `eval_binop` effectively handles all overflow/underflow calculations. if (lu && ru) { force_unsigned = !lu->type().IsSigned() || !ru->type().IsSigned(); if (is_comparison_op(op.op)) { return make_boolean(ast_, lu->value, ru->value, op); } result = eval_binop(lu->value, ru->value, op.op); } else if (ls && rs) { if (is_comparison_op(op.op)) { return make_boolean(ast_, ls->value, rs->value, op); } result = eval_binop(ls->value, rs->value, op.op); } else if (lu && rs) { force_unsigned = !lu->type().IsSigned(); if (lu->value <= std::numeric_limits::max()) { if (is_comparison_op(op.op)) { return make_boolean( ast_, static_cast(lu->value), rs->value, op); } result = eval_binop(static_cast(lu->value), rs->value, op.op); } } else if (ls && ru) { force_unsigned = !ru->type().IsSigned(); if (ru->value <= std::numeric_limits::max()) { if (is_comparison_op(op.op)) { return make_boolean( ast_, ls->value, static_cast(ru->value), op); } result = eval_binop(ls->value, static_cast(ru->value), op.op); } } else { // This is not an integer expression at all. return std::nullopt; } if (!result) { // This is not a valid expression. op.addError() << FLOW_ERROR; return std::nullopt; } return std::visit( [&](const auto &v) -> Expression { if constexpr (std::is_same_v, uint64_t>) { return ast_.make_node(v, Location(op.loc), force_unsigned); } else { return ast_.make_node(v, Location(op.loc)); } }, result.value()); } std::optional LiteralFolder::visit(Ternary &op) { visit(op.cond); visit(op.left); visit(op.right); if (op.cond.is()) { if (op.cond.as()->value != 0) { return op.left; } else { return op.right; } } else if (op.cond.is()) { return op.left; } else if (op.cond.is()) { if (op.cond.as()->value) { return op.left; } else { return op.right; } } else if (op.cond.is()) { if (!op.cond.as()->value.empty()) { return op.left; } else { return op.right; } } return std::nullopt; } std::optional LiteralFolder::visit(PositionalParameterCount ¶m) { if (!bpftrace_) { return std::nullopt; } // This is always an unsigned integer value. return ast_.make_node(bpftrace_->get().num_params(), Location(param.loc), /*force_unsigned=*/true); } std::optional LiteralFolder::visit(PositionalParameter ¶m) { if (!bpftrace_) { return std::nullopt; } // By default, we treat parameters as integer literals if we can, and // rely on the user to have an explicit `str` cast. const std::string &val = bpftrace_->get().get_param(param.n); // If empty, treat as zero. This is the documented behavior. if (val.empty()) { param.addWarning() << "Positional parameter $" << param.n << " is empty or not provided. "; return ast_.make_node(static_cast(0), Location(param.loc)); } if (val[0] == '-') { auto v = util::to_int(val); if (!v) { // Not parsed, treat it as a string. return ast_.make_node(val, Location(param.loc)); } return ast_.make_node(*v, Location(param.loc)); } else { auto v = util::to_uint(val); if (!v) { // Not parsed, treat it as a string. return ast_.make_node(val, Location(param.loc)); } return ast_.make_node(*v, Location(param.loc)); } } std::optional LiteralFolder::visit(Call &call) { // If this is the string function, then we can evaluate the given literal // as a string (e.g. this covers str(0) and str($3)). if (call.func == "str" && !call.vargs.empty()) { std::string s; // First, we need to check if this directly wraps a positional parameter. // If yes, then we prevent it from expanding to zero as is the default. if (auto *param = call.vargs.at(0).as()) { if (!bpftrace_) { return std::nullopt; // Can't fold yet. } call.vargs[0] = ast_.make_node( bpftrace_->get().get_param(param->n), Location(param->loc)); } else if (auto *binop = call.vargs.at(0).as()) { auto *param = binop->left.as(); if (param && binop->op == Operator::PLUS) { if (!bpftrace_) { return std::nullopt; // Can't fold yet. } binop->left = ast_.make_node( bpftrace_->get().get_param(param->n), Location(param->loc)); } } // Now we can expand normally. Visitor>::visit(call); // If this is an integer of some kind, then fold it into a string. if (auto *n = call.vargs.at(0).as()) { std::stringstream ss; ss << n->value; s = ss.str(); } else if (auto *n = call.vargs.at(0).as()) { std::stringstream ss; ss << n->value; s = ss.str(); } else if (auto *str = call.vargs.at(0).as()) { s = str->value; } else { return std::nullopt; } // Handle optional truncation. if (call.vargs.size() >= 2) { if (auto *n = call.vargs.at(1).as()) { // Truncate the string. if (s.size() >= n->value) { s = s.substr(0, n->value); } } else { return std::nullopt; } } return ast_.make_node(s, Location(call.loc)); } else { // Visit normally. Visitor>::visit(call); } return std::nullopt; } std::optional LiteralFolder::visit(Expression &expr) { auto r = Visitor>::visit(expr.value); if (r) { expr.value = r->value; } return std::nullopt; } std::optional LiteralFolder::visit(Probe &probe) { top_level_node_ = &probe; return Visitor>::visit(probe); } std::optional LiteralFolder::visit(Builtin &builtin) { if (builtin.ident == "__builtin_usermode") { if (auto *probe = dynamic_cast(top_level_node_)) { for (auto *ap : probe->attach_points) { if (!ap->check_available(builtin.ident)) { auto probe_type = probetype(ap->provider); if (probe_type == ProbeType::special) { return ast_.make_node(1, Location(builtin.loc)); } return ast_.make_node(0, Location(builtin.loc)); } } } } return std::nullopt; } void fold(ASTContext &ast, Expression &expr) { LiteralFolder folder(ast); folder.visit(expr); } Pass CreateFoldLiteralsPass() { auto fn = [](ASTContext &ast, BPFtrace &b) { LiteralFolder folder(ast, b); folder.visit(ast.root); }; return Pass::create("FoldLiterals", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/fold_literals.h000066400000000000000000000006761506776124200212570ustar00rootroot00000000000000#pragma once #include "ast/ast.h" #include "ast/pass_manager.h" namespace bpftrace::ast { // Folds only a single expression. // // This may be used by subsequent passes when expressions are replaced by // literals. Note however, that it is up to the pass to ensure that the full // expression is recursively folded. void fold(ASTContext &ast, Expression &expr); // Fold all nodes. Pass CreateFoldLiteralsPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/import_scripts.cpp000066400000000000000000000036231506776124200220430ustar00rootroot00000000000000#include "ast/passes/import_scripts.h" #include "ast/ast.h" #include "ast/passes/resolve_imports.h" namespace bpftrace::ast { static void import_ast(ASTContext &ast, const ASTContext &other) { // Clone all map declarations, subfunctions, etc. into the primary AST. // Note that we may choose not to inline all definitions in the future, and // define that namespace-based resolution is used for e.g. macro expansion, // function matching, etc. But for now, we just support a trivial // expansion. ast.diagnostics().add(std::move(other.diagnostics())); if (other.root) { ast.root->c_definitions += other.root->c_definitions; for (const auto &decl : other.root->map_decls) { ast.root->map_decls.push_back(clone(ast, decl)); } for (const auto &fn : other.root->functions) { ast.root->functions.push_back(clone(ast, fn)); } for (const auto ¯o : other.root->macros) { ast.root->macros.push_back(clone(ast, macro)); } for (const auto &probe : other.root->probes) { ast.root->probes.push_back(clone(ast, probe)); } } } Pass CreateImportExternalScriptsPass() { return Pass::create("ImportExternalScripts", [](ASTContext &ast, Imports &imports) { for (const auto &[name, obj] : imports.scripts) { if (!obj.internal) { import_ast(ast, obj.ast); } } }); } Pass CreateImportInternalScriptsPass() { return Pass::create("ImportInternalScripts", [](ASTContext &ast, Imports &imports) { for (const auto &[name, obj] : imports.scripts) { if (obj.internal) { import_ast(ast, obj.ast); } } }); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/import_scripts.h000066400000000000000000000003661506776124200215110ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { // Requires the `Imports` from the `CreateResolveImports` pass. Pass CreateImportInternalScriptsPass(); Pass CreateImportExternalScriptsPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/link.cpp000066400000000000000000000067531506776124200177260ustar00rootroot00000000000000#include #include #include "ast/passes/codegen_llvm.h" #include "ast/passes/link.h" #include "ast/passes/resolve_imports.h" #include "bpfbytecode.h" #include "scopeguard.h" #include "util/temp.h" namespace bpftrace::ast { char LinkError::ID; void LinkError::log(llvm::raw_ostream &OS) const { OS << "linking " << origin_ << ": " << strerror(err_); } Pass CreateExternObjectPass() { return Pass::create("extern", [](Imports &imports) { BpfExternObjects result; for (const auto &[name, obj] : imports.objects) { result.objects.emplace_back(obj.path); } return result; }); } Pass CreateLinkPass() { return Pass::create( "link", [](BpfObject &obj, BpfExternObjects &ext) -> Result { // If there are no other objects to link, then just return our own. if (ext.objects.empty()) { return BpfBytecode{ obj.data }; } // Create a working directory. auto dir = util::TempDir::create(); if (!dir) { return dir.takeError(); } // Otherwise, dump the intermediate object. auto object = dir->create_file(); if (!object) { return object.takeError(); } auto ok = object->write_all(obj.data); if (!ok) { return ok.takeError(); } // Create an output file on disk. In the future, we may want to accept // some flags that allow this file to persist. auto output = dir->create_file(); if (!output) { return output.takeError(); } // In order to craft the final output, since we may have external maps // and probes, we delegate the heavy lifting to libbpf. First, we open a // new memfd as output, and add our top-level output to the linker. struct bpf_linker *linker = bpf_linker__new(output->path().c_str(), nullptr); if (linker == nullptr) { // Hopefully an empty 'origin' here is sufficient to distinguish the // case where this failed. I believe that it's likely to be ENOMEM or // something equally obvious to the user? return make_error("", errno); } SCOPE_EXIT { bpf_linker__free(linker); }; // Link in our own program. int rc = bpf_linker__add_file(linker, object->path().c_str(), nullptr); if (rc != 0) { return make_error(output->path().string(), errno); } // Next, we iterate through the list of link targets that we collected // from import statements. These are added to the link target one at a // time. for (auto &path : ext.objects) { int rc = bpf_linker__add_file(linker, path.c_str(), nullptr); if (rc != 0) { return make_error(path.string(), errno); } } // Finalize the linking, and free our underlying library handle. rc = bpf_linker__finalize(linker); if (rc != 0) { return make_error(output->path().string(), errno); } // Reload the final output and return it. std::ifstream file(output->path(), std::ios::binary); if (!file.is_open()) { return make_error(output->path().string(), errno); } std::vector data(std::istreambuf_iterator(file), {}); return BpfBytecode{ data }; }); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/link.h000066400000000000000000000013601506776124200173600ustar00rootroot00000000000000#pragma once #include #include "ast/pass_manager.h" #include "util/result.h" namespace bpftrace::ast { class BpfExternObjects : public ast::State<"bpf-extern"> { public: std::vector objects; }; // Produces a set of external objects, `BpfExternObjects`. Pass CreateExternObjectPass(); class LinkError : public ErrorInfo { public: LinkError(std::string origin, int err) : origin_(std::move(origin)), err_(err) {}; static char ID; void log(llvm::raw_ostream &OS) const override; private: std::string origin_; int err_; }; // Produces the final output `BpfBytecode` object from `BpfObject` and the // `BpfExternObjects` provided. Pass CreateLinkPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/macro_expansion.cpp000066400000000000000000000272761506776124200221610ustar00rootroot00000000000000#include #include #include #include "ast/ast.h" #include "ast/context.h" #include "ast/passes/macro_expansion.h" #include "ast/visitor.h" #include "bpftrace.h" #include "log.h" namespace bpftrace::ast { std::unordered_map collect_macros(ASTContext &ast) { std::unordered_map macros; for (Macro *macro : ast.root->macros) { if (macros.contains(macro->name)) { macro->addError() << "Redifinition of macro: " << macro->name; return macros; } std::unordered_set seen_mvars; std::unordered_set seen_mmaps; for (const auto &arg : macro->vargs) { if (auto *mvar = arg.as()) { auto inserted = seen_mvars.insert(mvar->ident); if (!inserted.second) { mvar->addError() << "Variable for macro argument has already been used: " << mvar->ident; return macros; } } else if (auto *mmap = arg.as()) { auto inserted = seen_mmaps.insert(mmap->ident); if (!inserted.second) { mmap->addError() << "Map for macro argument has already been used: " << mmap->ident; return macros; } } } macros[macro->name] = macro; } return macros; } class MacroExpander : public Visitor { public: MacroExpander(ASTContext &ast, const std::unordered_map ¯os, std::string macro_name = "", std::vector &¯o_stack = {}); using Visitor::visit; void visit(AssignVarStatement &assignment); void visit(Variable &var); void visit(VariableAddr &var_addr); void visit(VarDeclStatement &decl); void visit(Map &map); void visit(Expression &expr); // We can't add extra params with default values to the standard `visit` // because it then becomes ambiguous void replace_macro_call(Expression &expr, bool block_ok = false); void visit(Statement &stmt); std::optional expand(Macro ¯o, Call &call); std::optional expand(Macro ¯o, Identifier &ident); std::optional make_block_expr(Macro ¯o, StatementList &stmt_list, const Location &loc); private: ASTContext &ast_; const std::unordered_map ¯os_; const std::string macro_name_; bool is_recursive_call(const std::string ¯o_name, const Node &node); std::string get_new_var_ident(std::string original_ident); bool is_top_level() { return macro_stack_.empty(); } // Maps of macro map/var names -> callsite map/var names std::unordered_map maps_; std::unordered_map vars_; std::unordered_set renamed_vars_; std::unordered_map passed_exprs_; const std::vector macro_stack_; }; MacroExpander::MacroExpander( ASTContext &ast, const std::unordered_map ¯os, std::string macro_name, std::vector &¯o_stack) : ast_(ast), macros_(macros), macro_name_(std::move(macro_name)), macro_stack_(std::move(macro_stack)) { } void MacroExpander::visit(AssignVarStatement &assignment) { if (is_top_level()) { visit(assignment.expr); return; } auto *var = assignment.var(); // Don't rename any variable passed by reference if (!vars_.contains(var->ident)) { renamed_vars_.insert(var->ident); } if (std::holds_alternative(assignment.var_decl)) { visit(std::get(assignment.var_decl)); } else { visit(std::get(assignment.var_decl)); } visit(assignment.expr); } void MacroExpander::visit(VarDeclStatement &decl) { if (is_top_level()) { return; } auto *var = decl.var; if (vars_.contains(var->ident)) { decl.addError() << "Variable declaration shadows macro arg " << var->ident; return; } renamed_vars_.insert(var->ident); visit(decl.var); } void MacroExpander::visit(Variable &var) { if (is_top_level()) { return; } if (auto it = vars_.find(var.ident); it != vars_.end()) { var.ident = it->second; } else if (renamed_vars_.contains(var.ident)) { var.ident = get_new_var_ident(var.ident); } } void MacroExpander::visit(VariableAddr &var_addr) { visit(var_addr.var); } void MacroExpander::visit(Map &map) { if (is_top_level()) { return; } if (auto it = maps_.find(map.ident); it != maps_.end()) { map.ident = it->second; } else { map.addError() << "Unhygienic access to map: " << map.ident << ". Maps must be passed into the macro as arguments."; } } bool MacroExpander::is_recursive_call(const std::string ¯o_name, const Node &node) { for (size_t i = 0; i < macro_stack_.size(); ++i) { if (macro_stack_.at(i) == macro_name) { auto &err = node.addError(); err << "Recursive macro call detected. Call chain: "; for (; i < macro_stack_.size(); ++i) { err << macro_stack_.at(i) << " > "; } err << macro_name; return true; } } return false; } void MacroExpander::replace_macro_call(Expression &expr, bool block_ok) { auto *ident = expr.as(); auto *call = expr.as(); if (!ident && !call) { // Recursively expand the new expression, which may again contain macros... Visitor::visit(expr); return; } if (ident) { if (auto it = passed_exprs_.find(ident->ident); it != passed_exprs_.end()) { expr = it->second; // Create a new expander because we're visiting an expression passed into // the macro so it's not part of the surounding macro code and therefore // variables, maps, and idents in this expression shouldn't be modified or // checked MacroExpander expander(ast_, macros_); expander.visit(expr); return; } } const std::string &name = ident ? ident->ident : call->func; if (call) { for (auto &varg : call->vargs) { visit(varg); } } if (auto it = macros_.find(name); it != macros_.end()) { Macro *macro = it->second; if (is_recursive_call(name, expr.node())) { return; } if (std::holds_alternative(macro->block) && !block_ok) { auto &err = expr.node().addError(); err << "Macro '" << name << "' expanded to a block instead of a block " "expression. Try removing the semicolon from the " "end of the last statement in the macro body."; return; } auto next_macro_stack = macro_stack_; next_macro_stack.push_back(name); auto r = ident ? MacroExpander(ast_, macros_, name, std::move(next_macro_stack)) .expand(*macro, *ident) : MacroExpander(ast_, macros_, name, std::move(next_macro_stack)) .expand(*macro, *call); if (r) { expr.value = *r; } } } void MacroExpander::visit(Expression &expr) { replace_macro_call(expr); } void MacroExpander::visit(Statement &stmt) { auto *expr_stmt = stmt.as(); if (!expr_stmt) { Visitor::visit(stmt); return; } replace_macro_call(expr_stmt->expr, true); } std::string MacroExpander::get_new_var_ident(std::string original_ident) { return std::string("$$") + macro_name_ + std::string("_") + original_ident; } std::optional MacroExpander::make_block_expr( Macro ¯o, StatementList &stmt_list, const Location &loc) { Expression macro_expr; if (std::holds_alternative(macro.block)) { auto *bare_block = std::get(macro.block); // Since this always evaluates to a BlockExpr we insert a unused // final expression. This shouldn't ever be an issue as we have // a check above to ensure that macro bodies that are blocks // never get used in place of block expressions, e.g., this is not legal // because there is a trailing semi-colon in the macro body: // `macro add_one($x) { $x + 1; } begin { $x = 1; $y = add_one($x);` macro_expr = ast_.make_node(false, Location(macro.loc)); for (auto expr : bare_block->stmts) { stmt_list.push_back(clone(ast_, expr, loc)); } } else { auto *block_expr = std::get(macro.block); macro_expr = clone(ast_, block_expr->expr, loc); for (auto expr : block_expr->stmts) { stmt_list.push_back(clone(ast_, expr, loc)); } } auto *cloned_block = ast_.make_node(std::move(stmt_list), macro_expr, Location(macro.loc)); visit(cloned_block); if (ast_.diagnostics().ok()) { return cloned_block; } return std::nullopt; } std::optional MacroExpander::expand(Macro ¯o, Call &call) { if (macro.vargs.size() != call.vargs.size()) { call.addError() << "Call to macro has wrong number arguments. Expected: " << macro.vargs.size() << " but got " << call.vargs.size(); return std::nullopt; } StatementList stmt_list; for (size_t i = 0; i < macro.vargs.size(); i++) { if (auto *mident = macro.vargs.at(i).as()) { if (call.vargs.at(i).is() || call.vargs.at(i).is()) { // Wrap variables and maps in a BlockExpr so their value is used // and they won't be mutated. passed_exprs_[mident->ident] = ast_.make_node( StatementList({}), clone(ast_, call.vargs.at(i), call.vargs.at(i).loc()), Location(call.loc)); } else { passed_exprs_[mident->ident] = clone(ast_, call.vargs.at(i), call.vargs.at(i).loc()); } } else if (auto *mvar = macro.vargs.at(i).as()) { if (auto *cvar = call.vargs.at(i).as()) { vars_[mvar->ident] = cvar->ident; } else if (call.vargs.at(i).is()) { call.addError() << "Mismatched arg to macro call. Macro expects a variable for arg " << mvar->ident << " but got a map."; } else { call.addError() << "Mismatched arg to macro call. Macro expects a variable for arg " << mvar->ident << " but got an expression."; } } else if (auto *mmap = macro.vargs.at(i).as()) { if (auto *cmap = call.vargs.at(i).as()) { maps_[mmap->ident] = cmap->ident; } else if (call.vargs.at(i).is()) { call.addError() << "Mismatched arg to macro call. Macro expects a map for arg " << mmap->ident << " but got a variable."; } else { call.addError() << "Mismatched arg to macro call. Macro expects a map for arg " << mmap->ident << " but got an expression."; } } } return make_block_expr(macro, stmt_list, call.loc); } std::optional MacroExpander::expand(Macro ¯o, Identifier &ident) { if (!macro.vargs.empty()) { ident.addError() << "Call to macro has no number arguments. Expected: " << macro.vargs.size(); return std::nullopt; } StatementList stmt_list; return make_block_expr(macro, stmt_list, ident.loc); } Pass CreateMacroExpansionPass() { auto fn = [](ASTContext &ast) { auto macros = collect_macros(ast); if (ast.diagnostics().ok()) { MacroExpander expander(ast, macros); expander.visit(ast.root); } }; return Pass::create("MacroExpansion", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/macro_expansion.h000066400000000000000000000002071506776124200216070ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateMacroExpansionPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/map_sugar.cpp000066400000000000000000000233631506776124200207430ustar00rootroot00000000000000#include #include "ast/ast.h" #include "ast/passes/map_sugar.h" #include "ast/visitor.h" namespace bpftrace::ast { const std::unordered_set &getAssignRewriteFuncs() { // Similarly these are syntactic sugar over operating on a map. This list // could also be dynamically generated based on some underlying annotation. static std::unordered_set ASSIGN_REWRITE = { "hist", "lhist", "count", "sum", "min", "max", "avg", "stats", "tseries", }; return ASSIGN_REWRITE; } namespace { class MapDefaultKey : public Visitor { public: explicit MapDefaultKey(ASTContext &ast) : ast_(ast) {}; using Visitor::visit; void visit(Call &call); void visit(For &for_loop); void visit(Map &map); void visit(MapAccess &acc); void visit(MapAddr &map_addr); void visit(AssignScalarMapStatement &assign); void visit(AssignMapStatement &assign); void visit(Expression &expr); void visit(Statement &stmt); [[nodiscard]] bool check(Map &map, bool indexed); void checkAccess(Map &map, bool indexed); void checkCall(Map &map, bool indexed, Call &call); MapMetadata metadata; private: ASTContext &ast_; }; class MapFunctionAliases : public Visitor { public: using Visitor::visit; void visit(Call &call); }; class MapAssignmentCall : public Visitor { public: explicit MapAssignmentCall(ASTContext &ast) : ast_(ast) {}; using Visitor::visit; void visit(Statement &stmt); private: ASTContext &ast_; }; class MapAssignmentCheck : public Visitor { public: using Visitor::visit; void visit(Call &call); }; } // namespace // These are special functions which are part of the map API, and operate // independently of any key. We allow the first argument to be a pure map, and // therefore don't expand a default key in these cases. // // In the future, this could be generalized by extracting information about the // specific function being called, and potentially respecting annotations on // these arguments. static std::unordered_set RAW_MAP_ARG = { "print", "clear", "zero", "len", "delete", "has_key", }; void MapDefaultKey::visit(Map &map) { checkAccess(map, false); } void MapDefaultKey::visit(MapAccess &acc) { checkAccess(*acc.map, true); visit(acc.key); } void MapDefaultKey::visit([[maybe_unused]] MapAddr &map_addr) { // Don't desugar this into a map access, we want the map pointer } void MapDefaultKey::visit(AssignScalarMapStatement &assign) { checkAccess(*assign.map, false); visit(assign.expr); } void MapDefaultKey::visit(AssignMapStatement &assign) { checkAccess(*assign.map, true); visit(assign.key); visit(assign.expr); } void MapDefaultKey::visit(Expression &expr) { Visitor::visit(expr); // Replace with an indexed map. Note that we don't visit for calls that // are exempt from this. This applies to all expressions in the tree, // including `lhist` and `hist`, which are treated in a special way // subsequently. if (auto *map = expr.as()) { auto *index = ast_.make_node(0, Location(map->loc)); expr.value = ast_.make_node(map, index, Location(map->loc)); } } void MapDefaultKey::visit(Statement &stmt) { Visitor::visit(stmt); // Replace with a statement that has the default index, in the same way as // above. This will be type-checked during semantic analysis. if (auto *map = stmt.as()) { auto *index = ast_.make_node(0, Location(map->loc)); stmt.value = ast_.make_node( map->map, index, map->expr, Location(map->loc)); } } bool MapDefaultKey::check(Map &map, bool indexed) { bool scalar = !indexed; auto val = metadata.scalar.find(map.ident); if (val == metadata.scalar.end()) { metadata.scalar.emplace(map.ident, scalar); return true; } else { return val->second == scalar; } } void MapDefaultKey::checkAccess(Map &map, bool indexed) { if (!check(map, indexed)) { if (indexed) { map.addError() << map.ident << " used as a map with an explicit key (non-scalar map), " "previously used without an explicit key (scalar map)"; } else { map.addError() << map.ident << " used as a map without an explicit key (scalar map), previously " "used with an explicit key (non-scalar map)"; } } } void MapDefaultKey::checkCall(Map &map, bool indexed, Call &call) { if (!check(map, indexed)) { if (indexed) { map.addError() << "call to " << call.func << "() expects a map with explicit keys (non-scalar map)"; } else { map.addError() << "call to " << call.func << "() expects a map without explicit keys (scalar map)"; } } } void MapDefaultKey::visit(Call &call) { // Skip the first argument in these cases. This allows the argument to be // *either* a pure map, or a map access. Later passes will figure out what to // do with this, as they may have parametric behavior (as with print). if (RAW_MAP_ARG.contains(call.func) && !call.vargs.empty()) { if (auto *map = call.vargs.at(0).as()) { // Check our functions for consistency. These are effectively builtins // that require that map to have keys (conditionally for delete). if (call.func == "delete") { if (call.vargs.size() == 1) { // Inject the default key. checkCall(*map, false, call); auto *index = ast_.make_node(0, Location(map->loc)); call.vargs.emplace_back(index); } else if (call.vargs.size() == 2) { checkCall(*map, true, call); } else { // Unfortunately there's no good way to handle this after desugaring. // The actual `delete` function requires two arguments (fixed), but // we accept this weird form (and also the alias). call.addError() << "delete() requires 1 or 2 arguments (" << call.vargs.size() << " provided)"; } } else if (call.func == "has_key") { checkCall(*map, true, call); } else if (call.func == "len") { checkCall(*map, true, call); } } else { if (call.func == "delete") { // See above; always report this error. We don't allow the semantic // analyser to capture this, because it not be happy about the number // of arguments and we don't want to mislead users with that. call.vargs.at(0).node().addError() << "delete() expects a map argument"; } visit(call.vargs.at(0)); } for (size_t i = 1; i < call.vargs.size(); i++) { visit(call.vargs.at(i)); } } else { Visitor::visit(call); } } void MapDefaultKey::visit(For &for_loop) { if (auto *map = for_loop.iterable.as()) { if (!check(*map, true)) { map->addError() << map->ident << " has no explicit keys (scalar map), and " "cannot be used for iteration"; } } else { // If the map is used for the range in any way, it needs // to be desugared properly. visit(for_loop.iterable); } visit(for_loop.stmts); } void MapFunctionAliases::visit(Call &call) { // We expect semantics for delete that are `delete(@, key)`. We support an // old `delete(@[key])` syntax but rewrite this under the hood. if (call.func == "delete") { if (call.vargs.size() == 1) { if (auto *access = call.vargs.at(0).as()) { call.vargs.clear(); call.vargs.emplace_back(access->map); call.vargs.emplace_back(access->key); call.injected_args += 1; } } } } static std::optional injectMap(Expression expr, Map *map, Expression key) { if (auto *call = expr.as()) { if (getAssignRewriteFuncs().contains(call->func)) { auto args = std::move(call->vargs); call->vargs.emplace_back(map); call->vargs.emplace_back(key); call->injected_args += 2; call->vargs.insert(call->vargs.end(), args.begin(), args.end()); return call; } } else if (auto *block_expr = expr.as()) { auto injected_expr = injectMap(block_expr->expr, map, key); if (injected_expr) { return block_expr; } } return std::nullopt; } void MapAssignmentCall::visit(Statement &stmt) { // Any assignments that are direct calls to special functions may // be rewritten to simply be the function expression. if (auto *assign = stmt.as()) { auto expr = injectMap(assign->expr, assign->map, assign->key); if (expr) { // We injected a call, and can flatten the statement. stmt.value = ast_.make_node(expr.value(), Location(assign->loc)); } } Visitor::visit(stmt); } void MapAssignmentCheck::visit(Call &call) { if (getAssignRewriteFuncs().contains(call.func) && call.injected_args == 0) { call.addError() << call.func << "() must be assigned directly to a map"; } Visitor::visit(call); } Pass CreateMapSugarPass() { auto fn = [](ASTContext &ast) -> MapMetadata { MapFunctionAliases aliases; aliases.visit(ast.root); MapDefaultKey defaults(ast); defaults.visit(ast.root); if (!ast.diagnostics().ok()) { // No consistent defaults. return std::move(defaults.metadata); } MapAssignmentCall sugar(ast); sugar.visit(ast.root); MapAssignmentCheck check; check.visit(ast.root); return std::move(defaults.metadata); }; return Pass::create("MapSugar", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/map_sugar.h000066400000000000000000000011531506776124200204010ustar00rootroot00000000000000#pragma once #include #include "ast/pass_manager.h" namespace bpftrace::ast { // MapMetadata contains metadata related to the sugared maps. // // For now, this is whether they are used as scalars. In the future, this may // be used as the basis for `MapInfo`, which can be propagated and used by // passes, rather than being mutated within the BPFtrace object. class MapMetadata : public ast::State<"map-metadata"> { public: std::unordered_map scalar; }; const std::unordered_set& getAssignRewriteFuncs(); Pass CreateMapSugarPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/named_param.cpp000066400000000000000000000074011506776124200212240ustar00rootroot00000000000000#include "ast/passes/named_param.h" #include "ast/ast.h" #include "ast/context.h" #include "ast/visitor.h" #include "bpftrace.h" namespace bpftrace::ast { class NamedParamPass : public Visitor { public: NamedParamPass(ASTContext &ast, BPFtrace &bpftrace) : ast_(ast), bpftrace_(bpftrace) {}; using Visitor::visit; void visit(Expression &expr); std::unordered_map used_args; NamedParamDefaults defaults; private: ASTContext &ast_; BPFtrace &bpftrace_; }; void NamedParamPass::visit(Expression &expr) { auto *call = expr.as(); if (!call || call->func != "getopt") { Visitor::visit(expr); return; } auto *arg_name = call->vargs.at(0).as(); if (!arg_name) { call->vargs.at(0).node().addError() << "First argument to 'getopt' must be a string literal."; return; } if (call->vargs.size() == 2) { if (!call->vargs.at(1).as() && !call->vargs.at(1).as() && !call->vargs.at(1).as() && !call->vargs.at(1).as()) { call->vargs.at(1).node().addError() << "Second argument to 'getopt' must be a string literal, integer " "literal, or a boolean literal."; return; } } globalvars::GlobalVarValue np_default; auto *map_node = ast_.make_node(arg_name->value, Location(call->loc)); map_node->key_type = CreateInt64(); if (call->vargs.size() == 1) { // boolean map_node->value_type = CreateBool(); np_default = false; } else if (auto *default_value = call->vargs.at(1).as()) { // boolean map_node->value_type = CreateBool(); np_default = default_value->value; } else if (auto *default_value = call->vargs.at(1).as()) { // string map_node->value_type = CreateString(bpftrace_.config_->max_strlen); np_default = default_value->value; } else if (auto *default_value = call->vargs.at(1).as()) { // integer map_node->value_type = CreateInt64(); np_default = static_cast(default_value->value); } else if (auto *default_value = call->vargs.at(1).as()) { // integer map_node->value_type = CreateInt64(); np_default = default_value->value; } if (used_args.contains(arg_name->value) && used_args.at(arg_name->value) != np_default) { std::string pre_value; if (std::holds_alternative(used_args.at(arg_name->value))) { pre_value = std::get(used_args.at(arg_name->value)); } else if (std::holds_alternative(used_args.at(arg_name->value))) { pre_value = std::to_string( std::get(used_args.at(arg_name->value))); } else { pre_value = std::get(used_args.at(arg_name->value)) ? "true" : "false"; } call->addError() << "Command line option '" << arg_name->value << "' needs to have the same default value in all places " "it is used. Previous default value: " << pre_value; return; } auto *index = ast_.make_node(0, Location(map_node->loc)); expr.value = ast_.make_node(map_node, index, Location(map_node->loc)); used_args[arg_name->value] = np_default; defaults.defaults[arg_name->value] = std::move(np_default); } Pass CreateNamedParamsPass() { auto fn = [](ASTContext &ast, BPFtrace &b) -> Result { NamedParamPass np_pass(ast, b); np_pass.visit(ast.root); return std::move(np_pass.defaults); }; return Pass::create("NamedParam", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/named_param.h000066400000000000000000000005131506776124200206660ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" #include "globalvars.h" #include "types.h" namespace bpftrace::ast { class NamedParamDefaults : public ast::State<"named_params_defaults"> { public: std::unordered_map defaults; }; Pass CreateNamedParamsPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/parser.h000066400000000000000000000042021506776124200177150ustar00rootroot00000000000000#pragma once #include "ast/attachpoint_parser.h" #include "ast/pass_manager.h" #include "ast/passes/c_macro_expansion.h" #include "ast/passes/clang_parser.h" #include "ast/passes/config_analyser.h" #include "ast/passes/deprecated.h" #include "ast/passes/field_analyser.h" #include "ast/passes/import_scripts.h" #include "ast/passes/macro_expansion.h" #include "ast/passes/map_sugar.h" #include "ast/passes/named_param.h" #include "ast/passes/probe_expansion.h" #include "ast/passes/resolve_imports.h" #include "ast/passes/unstable_feature.h" #include "btf.h" #include "driver.h" #include "tracepoint_format_parser.h" namespace bpftrace::ast { // AllParsePasses returns a vector of passes representing all parser passes, in // the expected order. This should be used unless there's a reason not to. inline std::vector AllParsePasses( std::vector &&extra_flags = {}, std::vector &&import_paths = {}, bool debug = false) { std::vector passes; passes.emplace_back(CreateParsePass(0, debug)); passes.emplace_back(CreateConfigPass()); passes.emplace_back(CreateResolveImportsPass(std::move(import_paths))); // N.B. We expand the AST with all externally imported scripts, then check // against unstable features, *then* import all internal scripts. This means // that internal scripts are except from the unstable feature warning. passes.emplace_back(CreateImportExternalScriptsPass()); passes.emplace_back(CreateUnstableFeaturePass()); passes.emplace_back(CreateImportInternalScriptsPass()); passes.emplace_back(CreateMacroExpansionPass()); passes.emplace_back(CreateDeprecatedPass()); passes.emplace_back(CreateParseAttachpointsPass()); passes.emplace_back(CreateParseBTFPass()); passes.emplace_back(CreateProbeExpansionPass()); passes.emplace_back(CreateParseTracepointFormatPass()); passes.emplace_back(CreateFieldAnalyserPass()); passes.emplace_back(CreateClangParsePass(std::move(extra_flags))); passes.emplace_back(CreateCMacroExpansionPass()); passes.emplace_back(CreateMapSugarPass()); passes.emplace_back(CreateNamedParamsPass()); return passes; } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/pid_filter_pass.cpp000066400000000000000000000052261506776124200221320ustar00rootroot00000000000000#include "ast/passes/pid_filter_pass.h" #include "ast/ast.h" #include "ast/visitor.h" #include "bpftrace.h" namespace bpftrace::ast { namespace { class PidFilterPass : public Visitor { public: explicit PidFilterPass(ASTContext &ast, BPFtrace &bpftrace) : ast_(ast), bpftrace_(bpftrace) { } using Visitor::visit; void visit(Probe &probe); private: ASTContext &ast_; BPFtrace &bpftrace_; }; // If the probe can't filter by pid when attaching // then we inject custom AST to filter by pid. // Note: this doesn't work for AOT as the code has already // been generated bool probe_needs_pid_filter(AttachPoint *ap) { ProbeType type = probetype(ap->provider); switch (type) { case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::tracepoint: case ProbeType::rawtracepoint: return true; case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::invalid: case ProbeType::iter: case ProbeType::profile: case ProbeType::software: case ProbeType::hardware: // We don't filter by pid at all for these special probes case ProbeType::interval: case ProbeType::special: case ProbeType::benchmark: return false; } return false; } } // namespace static Statement create_pid_filter(ASTContext &ast, int pid, const Location &loc) { return ast.make_node( ast.make_node(ast.make_node("pid", Location(loc)), Operator::NE, ast.make_node(pid, Location(loc)), Location(loc)), ast.make_node(std::vector{ ast.make_node( JumpType::RETURN, Location(loc)) }, Location(loc)), ast.make_node(std::vector{}, Location(loc)), Location(loc)); } void PidFilterPass::visit(Probe &probe) { const auto pid = bpftrace_.pid(); if (!pid.has_value()) { return; } for (AttachPoint *ap : probe.attach_points) { if (probe_needs_pid_filter(ap)) { probe.block->stmts.insert(probe.block->stmts.begin(), create_pid_filter(ast_, *pid, probe.loc)); return; } } } Pass CreatePidFilterPass() { return Pass::create("PidFilter", [](ASTContext &ast, BPFtrace &b) { auto pid_filter = PidFilterPass(ast, b); pid_filter.visit(ast.root); }); }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/pid_filter_pass.h000066400000000000000000000002021506776124200215640ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreatePidFilterPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/portability_analyser.cpp000066400000000000000000000105151506776124200232200ustar00rootroot00000000000000#include #include "ast/passes/portability_analyser.h" #include "ast/visitor.h" #include "types.h" namespace bpftrace::ast { namespace { // 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: using Visitor::visit; void visit(PositionalParameter ¶m); void visit(Builtin &builtin); void visit(Call &call); void visit(Cast &cast); void visit(AttachPoint &ap); }; 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 param.addError() << "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 == "__builtin_curtask") { builtin.addError() << "AOT does not yet support accessing `curtask`"; } } void PortabilityAnalyser::visit(Call &call) { for (auto &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") { call.addError() << "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. cast.addError() << "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) { ap.addError() << "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) { ap.addError() << "AOT does not yet support watchpoint probes"; } } } // namespace Pass CreatePortabilityPass() { auto fn = [](ASTContext &ast) { PortabilityAnalyser analyser; analyser.visit(ast.root); if (!ast.diagnostics().ok()) { // 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 Pass::create("PortabilityAnalyser", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/portability_analyser.h000066400000000000000000000002041506776124200226570ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreatePortabilityPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/printer.cpp000066400000000000000000000273641506776124200204550ustar00rootroot00000000000000#include "ast/passes/printer.h" #include #include #include #include "ast/ast.h" #include "struct.h" namespace bpftrace::ast { std::string Printer::type(const SizedType &ty) { if (ty.IsNoneTy()) return ""; std::stringstream buf; buf << " :: [" << typestr(ty, true); 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.value << type(integer.integer_type) << std::endl; } void Printer::visit(NegativeInteger &integer) { std::string indent(depth_, ' '); out_ << indent << "negative int: " << integer.value << std::endl; } void Printer::visit(Boolean &boolean) { std::string indent(depth_, ' '); out_ << indent << "bool: " << (boolean.value ? "true" : "false") << std::endl; } void Printer::visit(PositionalParameter ¶m) { std::string indent(depth_, ' '); out_ << indent << "param: $" << param.n << std::endl; } void Printer::visit([[maybe_unused]] PositionalParameterCount ¶m) { std::string indent(depth_, ' '); out_ << indent << "param: $#" << std::endl; } void Printer::visit(String &string) { std::string indent(depth_, ' '); std::stringstream ss; for (char c : string.value) { // 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() << std::endl; } void Printer::visit(Builtin &builtin) { std::string indent(depth_, ' '); out_ << indent << "builtin: " << builtin.ident << type(builtin.builtin_type) << std::endl; } void Printer::visit(Identifier &identifier) { std::string indent(depth_, ' '); out_ << indent << "identifier: " << identifier.ident << type(identifier.ident_type) << std::endl; } void Printer::visit(Call &call) { std::string indent(depth_, ' '); out_ << indent << "call: " << call.func << type(call.return_type) << std::endl; ++depth_; visit(call.vargs); --depth_; } void Printer::visit(Sizeof &szof) { std::string indent(depth_, ' '); out_ << indent << "sizeof: " << std::endl; ++depth_; visit(szof.record); --depth_; } void Printer::visit(Offsetof &offof) { std::string indent(depth_, ' '); out_ << indent << "offsetof: " << std::endl; ++depth_; std::string indentParam(depth_, ' '); // Print the args if (std::holds_alternative(offof.record)) { visit(std::get(offof.record)); } else { out_ << indentParam << std::get(offof.record) << std::endl; } for (const auto &field : offof.field) { out_ << indentParam << field << std::endl; } --depth_; } void Printer::visit(MapDeclStatement &decl) { std::string indent(depth_, ' '); out_ << indent << "map decl: " << decl.ident << std::endl; ++depth_; std::string indentType(depth_, ' '); out_ << indentType << "bpf type: " << decl.bpf_type << std::endl; out_ << indentType << "max entries: " << decl.max_entries << std::endl; --depth_; } void Printer::visit(Map &map) { // Use a slightly customized format for the map type here, since it is never // going to be marked as `is_ctx` or have an associated address space. std::string indent(depth_, ' '); out_ << indent << "map: " << map.ident; if (!map.key_type.IsNoneTy() || !map.value_type.IsNoneTy()) { out_ << " :: "; } if (!map.key_type.IsNoneTy()) { out_ << "[" << typestr(map.key_type, true) << "]"; } if (!map.value_type.IsNoneTy()) { out_ << typestr(map.value_type, true); } out_ << std::endl; } void Printer::visit(MapAddr &map_addr) { std::string indent(depth_, ' '); out_ << indent << "&" << std::endl; ++depth_; visit(map_addr.map); --depth_; } void Printer::visit(Variable &var) { std::string indent(depth_, ' '); out_ << indent << "variable: " << var.ident << type(var.var_type) << std::endl; } void Printer::visit(VariableAddr &var_addr) { std::string indent(depth_, ' '); out_ << indent << "&" << std::endl; ++depth_; visit(var_addr.var); --depth_; } void Printer::visit(Binop &binop) { std::string indent(depth_, ' '); out_ << indent << opstr(binop) << type(binop.result_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.result_type) << std::endl; ++depth_; visit(unop.expr); --depth_; } void Printer::visit(Ternary &ternary) { std::string indent(depth_, ' '); out_ << indent << "?:" << type(ternary.result_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.field_type) << std::endl; ++depth_; visit(acc.expr); --depth_; out_ << indent << " " << acc.field << std::endl; } void Printer::visit(ArrayAccess &arr) { std::string indent(depth_, ' '); out_ << indent << "[]" << type(arr.element_type) << std::endl; ++depth_; visit(arr.expr); visit(arr.indexpr); --depth_; } void Printer::visit(TupleAccess &acc) { std::string indent(depth_, ' '); out_ << indent << "." << type(acc.element_type) << std::endl; ++depth_; visit(acc.expr); --depth_; out_ << indent << " " << acc.index << std::endl; } void Printer::visit(MapAccess &acc) { std::string indent(depth_, ' '); out_ << indent << "[]" << type(acc.type()) << std::endl; ++depth_; visit(acc.map); visit(acc.key); --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(AssignScalarMapStatement &assignment) { std::string indent(depth_, ' '); out_ << indent << "=" << std::endl; ++depth_; visit(assignment.map); visit(assignment.expr); --depth_; } void Printer::visit(AssignMapStatement &assignment) { std::string indent(depth_, ' '); out_ << indent << "=" << std::endl; ++depth_; visit(assignment.map); ++depth_; visit(assignment.key); --depth_; visit(assignment.expr); --depth_; } void Printer::visit(AssignVarStatement &assignment) { std::string indent(depth_, ' '); if (std::holds_alternative(assignment.var_decl)) { visit(std::get(assignment.var_decl)); ++depth_; visit(assignment.expr); --depth_; } else { out_ << indent << "=" << std::endl; ++depth_; visit(std::get(assignment.var_decl)); visit(assignment.expr); --depth_; } } void Printer::visit(AssignConfigVarStatement &assignment) { std::string indent(depth_, ' '); out_ << indent << "=" << std::endl; ++depth_; std::string indentVar(depth_, ' '); out_ << indentVar << "var: " << assignment.var << std::endl; std::visit( [&](auto &v) { if constexpr (std::is_same_v, std::string>) { out_ << indentVar << "string: " << v << std::endl; } else if constexpr (std::is_same_v, bool>) { out_ << indentVar << "bool: " << (v ? "true" : "false") << std::endl; } else { out_ << indentVar << "int: " << v << std::endl; } }, assignment.value); --depth_; } void Printer::visit(VarDeclStatement &decl) { std::string indent(depth_, ' '); if (decl.type) { out_ << indent << "decl" << type(*decl.type) << std::endl; } else { 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(Range &range) { std::string indent(depth_, ' '); out_ << indent << "start\n"; ++depth_; visit(range.start); --depth_; out_ << indent << "end\n"; ++depth_; visit(range.end); --depth_; } 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); visit(for_loop.iterable); --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(SubprogArg &arg) { std::string indent(depth_, ' '); ++depth_; out_ << indent << arg.name << type(arg.type) << std::endl; --depth_; } void Printer::visit(Subprog &subprog) { std::string indent(depth_, ' '); out_ << indent << "subprog: " << subprog.name << type(subprog.return_type) << std::endl; ++depth_; if (!subprog.args.empty()) { ++depth_; out_ << indent << " args" << std::endl; visit(subprog.args); --depth_; } visit(subprog.stmts); --depth_; } void Printer::visit(Import &imp) { std::string indent(depth_, ' '); out_ << indent << "import " << imp.name << std::endl; } void Printer::visit(Program &program) { if (!program.c_definitions.empty()) out_ << program.c_definitions << std::endl; std::string indent(depth_, ' '); out_ << indent << "Program" << std::endl; ++depth_; visit(program.config); --depth_; ++depth_; visit(program.imports); --depth_; ++depth_; visit(program.map_decls); --depth_; ++depth_; visit(program.functions); visit(program.probes); --depth_; } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/printer.h000066400000000000000000000035241506776124200201120ustar00rootroot00000000000000#pragma once #include #include "ast/visitor.h" namespace bpftrace::ast { class Printer : public Visitor { public: explicit Printer(std::ostream &out) : out_(out) { } using Visitor::visit; void visit(Integer &integer); void visit(NegativeInteger &integer); void visit(Boolean &boolean); void visit(PositionalParameter ¶m); void visit(PositionalParameterCount ¶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(MapAddr &map_addr); void visit(MapDeclStatement &decl); void visit(Variable &var); void visit(VariableAddr &var_addr); void visit(Binop &binop); void visit(Unop &unop); void visit(Ternary &ternary); void visit(FieldAccess &acc); void visit(ArrayAccess &arr); void visit(TupleAccess &acc); void visit(MapAccess &acc); void visit(Cast &cast); void visit(Tuple &tuple); void visit(ExprStatement &expr); void visit(AssignMapStatement &assignment); void visit(AssignScalarMapStatement &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(Range &range); 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(SubprogArg &arg); void visit(Subprog &subprog); void visit(Import &imp); void visit(Program &program); private: std::ostream &out_; int depth_ = 0; std::string type(const SizedType &ty); }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/probe_expansion.cpp000066400000000000000000000232231506776124200221530ustar00rootroot00000000000000#include "ast/passes/probe_expansion.h" #include #include "ast/visitor.h" #include "bpftrace.h" #include "log.h" #include "util/wildcard.h" namespace bpftrace::ast { class ExpansionAnalyser : public Visitor { public: ExpansionAnalyser(BPFtrace &bpftrace) : bpftrace_(bpftrace) { } ExpansionResult analyse(Program &program); using Visitor::visit; void visit(Probe &probe); void visit(AttachPoint &ap); void visit(Builtin &builtin); private: ExpansionResult result_; Probe *probe_ = nullptr; BPFtrace &bpftrace_; }; ExpansionResult ExpansionAnalyser::analyse(Program &program) { visit(program); return std::move(result_); } void ExpansionAnalyser::visit(Probe &probe) { probe_ = &probe; visit(probe.attach_points); visit(probe.pred); visit(probe.block); } void ExpansionAnalyser::visit(AttachPoint &ap) { ExpansionType expansion = ExpansionType::NONE; switch (probetype(ap.provider)) { case ProbeType::kprobe: case ProbeType::kretprobe: // kprobe_multi does not support the "module:function" syntax so in case // a module is specified, always use full expansion if (util::has_wildcard(ap.target)) { expansion = ExpansionType::FULL; } else if (util::has_wildcard(ap.func)) { if (ap.target.empty() && bpftrace_.feature_->has_kprobe_multi()) expansion = ExpansionType::MULTI; else expansion = ExpansionType::FULL; } break; case ProbeType::uprobe: case ProbeType::uretprobe: // As the C++ language supports function overload, a given function name // (without parameters) could have multiple matches even when no // wildcards are used. if (util::has_wildcard(ap.func) || util::has_wildcard(ap.target) || ap.lang == "cpp") { if (bpftrace_.feature_->has_uprobe_multi()) expansion = ExpansionType::MULTI; else expansion = ExpansionType::FULL; } break; case ProbeType::fentry: case ProbeType::fexit: { if (ap.target == "bpf") { if (!ap.bpf_prog_id || util::has_wildcard(ap.func)) { expansion = ExpansionType::FULL; } break; } [[fallthrough]]; } case ProbeType::tracepoint: case ProbeType::rawtracepoint: if (util::has_wildcard(ap.target) || util::has_wildcard(ap.func)) expansion = ExpansionType::FULL; break; case ProbeType::usdt: // Always fully expand USDT probes as they may access args if (util::has_wildcard(ap.target) || util::has_wildcard(ap.ns) || ap.ns.empty() || util::has_wildcard(ap.func) || bpftrace_.pid().has_value()) { expansion = ExpansionType::FULL; } break; case ProbeType::watchpoint: if (util::has_wildcard(ap.func)) expansion = ExpansionType::FULL; break; case ProbeType::iter: if (util::has_wildcard(ap.func)) expansion = ExpansionType::FULL; default: // No expansion support for the rest of the probe types break; } if (expansion != ExpansionType::NONE) result_.set_expansion(ap, expansion); } void ExpansionAnalyser::visit(Builtin &builtin) { if (!probe_) return; if (builtin.ident == "__builtin_probe") { for (auto *ap : probe_->attach_points) result_.set_expansion(*ap, ExpansionType::FULL); } } class SessionExpander : public Visitor { public: explicit SessionExpander(ASTContext &ast, BPFtrace &bpftrace, ExpansionResult &expansion_result) : ast_(ast), bpftrace_(bpftrace), expansion_result_(expansion_result) { } using Visitor::visit; void visit(Probe &probe); Probe expand(Probe &entry, Probe &exit); private: Probe *find_matching_retprobe(Probe &probe); ASTContext &ast_; const BPFtrace &bpftrace_; ExpansionResult &expansion_result_; }; Probe *SessionExpander::find_matching_retprobe(Probe &probe) { ProbeList retprobes; AttachPoint *ap = probe.attach_points[0]; // Search for a probe which: // - has a single kretprobe attach point // - attaches to the same target and function as probe // - is multi-expanded (session expansion uses the same attach mechanism) // - has no predicate std::ranges::copy_if( ast_.root->probes, std::back_inserter(retprobes), [&](Probe *other) { return other->attach_points.size() == 1 && other->pred == nullptr && probetype(other->attach_points[0]->provider) == ProbeType::kretprobe && expansion_result_.get_expansion(*other->attach_points[0]) == ExpansionType::MULTI && other->attach_points[0]->target == ap->target && other->attach_points[0]->func == ap->func; }); // If there's not exactly one match, we don't know how to do session expansion if (retprobes.size() == 1) return retprobes[0]; return nullptr; } void SessionExpander::visit(Probe &probe) { // If the probe has a single multi-expanded kprobe attach point, check if // there's another probe with a single multi-expanded kretprobe attach point // with the same target. If so, perform session expansion by merging the two // probes together. // Currently, we don't allow predicates in either of the probes. if (probe.attach_points.size() == 1 && probetype(probe.attach_points[0]->provider) == ProbeType::kprobe && probe.pred == nullptr) { Probe *retprobe = find_matching_retprobe(probe); if (!retprobe) return; if (!bpftrace_.feature_->has_kprobe_session()) return; AttachPointList attach_points = probe.attach_points; auto *if_cond = ast_.make_node( ast_.make_node("__builtin_session_is_return", Location(probe.block->loc)), retprobe->block, probe.block, Location(probe.block->loc)); probe.block = ast_.make_node(std::vector{ if_cond }, Location(probe.block->loc)); expansion_result_.set_expansion(*probe.attach_points[0], ExpansionType::SESSION); std::erase(ast_.root->probes, retprobe); } } class ProbeExpander : public Visitor { public: ProbeExpander(ASTContext &ast, BPFtrace &bpftrace, ExpansionResult &result) : ast_(ast), bpftrace_(bpftrace), result_(result) { } void expand(); using Visitor::visit; void visit(Program &prog); void visit(AttachPointList &aps); private: uint64_t probe_count_ = 0; ASTContext &ast_; BPFtrace &bpftrace_; ExpansionResult &result_; }; void ProbeExpander::expand() { visit(*ast_.root); } void ProbeExpander::visit(Program &prog) { Visitor::visit(prog); } void ProbeExpander::visit(AttachPointList &aps) { const auto max_bpf_progs = bpftrace_.config_->max_bpf_progs; AttachPointList new_aps; for (auto *ap : aps) { auto probe_type = probetype(ap->provider); auto expansion = result_.get_expansion(*ap); switch (expansion) { case ExpansionType::FULL: { auto matches = bpftrace_.probe_matcher_->get_matches_for_ap(*ap); probe_count_ += matches.size(); if (probe_count_ > max_bpf_progs) { auto &err = ap->addError(); err << "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); err.addHint() << "You can increase the limit through the " "BPFTRACE_MAX_BPF_PROGS " "environment variable."; return; } for (const auto &match : matches) { new_aps.push_back(ap->create_expansion_copy(ast_, match)); } break; } case ExpansionType::SESSION: case ExpansionType::MULTI: { auto matches = bpftrace_.probe_matcher_->get_matches_for_ap(*ap); if (util::has_wildcard(ap->target)) { // If we have a wildcard in the target path, we need to generate one // attach point per expanded target assert(probe_type == ProbeType::uprobe || probe_type == ProbeType::uretprobe); std::unordered_map new_aps_by_target; for (const auto &func : matches) { auto *match_ap = ap->create_expansion_copy(ast_, func); // Reset the original (possibly wildcarded) function name auto expanded_func = match_ap->func; match_ap->func = ap->func; auto new_ap = new_aps_by_target.emplace(match_ap->target, match_ap); result_.add_expanded_func(*new_ap.first->second, match_ap->target + ":" + expanded_func); } for (auto &[_, new_ap] : new_aps_by_target) new_aps.push_back(std::move(new_ap)); } else if (!matches.empty()) { result_.set_expanded_funcs(*ap, std::move(matches)); new_aps.push_back(ap); } break; } case ExpansionType::NONE: { new_aps.push_back(ap); break; } } } aps = new_aps; } Pass CreateProbeExpansionPass() { auto fn = [](ASTContext &ast, BPFtrace &bpftrace) { ExpansionAnalyser analyser(bpftrace); auto result = analyser.analyse(*ast.root); SessionExpander session_expander(ast, bpftrace, result); session_expander.visit(*ast.root); ProbeExpander expander(ast, bpftrace, result); expander.expand(); return result; }; return Pass::create("ProbeExpansion", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/probe_expansion.h000066400000000000000000000041441506776124200216210ustar00rootroot00000000000000#pragma once #include #include "ast/ast.h" #include "ast/pass_manager.h" namespace bpftrace::ast { // There are 3 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 // - session expansion - extension of the multi expansion when a single BPF // program is shared for both the entry and the exit probe // (when they are both attached to the same attach points) // using the kprobe.session mechanism enum class ExpansionType { NONE, FULL, MULTI, SESSION, }; class ExpansionResult : public State<"expansions"> { public: ExpansionResult() = default; ExpansionResult(const ExpansionResult &) = delete; ExpansionResult &operator=(const ExpansionResult &) = delete; ExpansionResult(ExpansionResult &&) = default; ExpansionResult &operator=(ExpansionResult &&) = default; void set_expansion(AttachPoint &ap, ExpansionType type) { expansions[&ap] = type; } ExpansionType get_expansion(AttachPoint &ap) { auto exp = expansions.find(&ap); if (exp == expansions.end()) return ExpansionType::NONE; return exp->second; } void set_expanded_funcs(AttachPoint &ap, std::set funcs) { expanded_funcs.emplace(&ap, std::move(funcs)); } void add_expanded_func(AttachPoint &ap, const std::string &func) { auto funcs = expanded_funcs.emplace(&ap, std::set()); funcs.first->second.insert(func); } std::set get_expanded_funcs(AttachPoint &ap) { auto funcs = expanded_funcs.find(&ap); if (funcs == expanded_funcs.end()) return {}; return funcs->second; } private: std::unordered_map expansions; std::unordered_map> expanded_funcs; }; Pass CreateProbeExpansionPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/probe_prune.cpp000066400000000000000000000021451506776124200213000ustar00rootroot00000000000000#include "ast/passes/probe_prune.h" #include "ast/ast.h" #include "ast/visitor.h" #include "bpftrace.h" #include "log.h" namespace bpftrace::ast { Pass CreateProbePrunePass() { static std::string missing_msg = " has no valid attach points."; return Pass::create("ProbePrune", [](ASTContext &ast, BPFtrace &b) { auto missing_config = b.config_->missing_probes; for (Probe *probe : ast.root->probes) { if (probe->attach_points.empty()) { if (missing_config == ConfigMissingProbes::error) { probe->addError() << "Probe" << missing_msg << " If this is expected, set the 'missing_probes' " "config variable to 'warn'."; } else if (missing_config == ConfigMissingProbes::warn) { LOG(WARNING) << probe->orig_name << missing_msg << " It is being removed which may cause issues with " "program behavior."; } } }; if (missing_config != ConfigMissingProbes::error) { ast.root->clear_empty_probes(); } }); }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/probe_prune.h000066400000000000000000000002031506776124200207360ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateProbePrunePass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/recursion_check.cpp000066400000000000000000000052411506776124200221260ustar00rootroot00000000000000#include #include "ast/ast.h" #include "ast/passes/recursion_check.h" #include "ast/visitor.h" #include "bpftrace.h" #include "log.h" namespace bpftrace::ast { namespace { const std::unordered_set RECURSIVE_KERNEL_FUNCS = { "vmlinux:_raw_spin_lock", "vmlinux:_raw_spin_lock_irqsave", "vmlinux:_raw_spin_unlock_irqrestore", "vmlinux:queued_spin_lock_slowpath", }; // 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.contains(func_name); } class RecursionCheck : public Visitor { public: explicit RecursionCheck(BPFtrace &bpftrace) : bpftrace_(bpftrace) { } using Visitor::visit; void visit(Program &program); private: BPFtrace &bpftrace_; }; } // namespace // 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 RecursionCheck::visit(Program &program) { for (auto *probe : program.probes) { for (auto *ap : probe->attach_points) { auto probe_type = probetype(ap->provider); if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit) { auto matches = bpftrace_.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."; bpftrace_.need_recursion_check_ = true; return; } } } } } } Pass CreateRecursionCheckPass() { return Pass::create("RecursionCheck", [](ASTContext &ast, BPFtrace &b) { auto recursion_check = RecursionCheck(b); recursion_check.visit(ast.root); }); }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/recursion_check.h000066400000000000000000000002071506776124200215700ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateRecursionCheckPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/resolve_imports.cpp000066400000000000000000000270261506776124200222210ustar00rootroot00000000000000#include #include #include #include "ast/passes/resolve_imports.h" #include "ast/visitor.h" #include "driver.h" #include "stdlib/stdlib.h" #include "util/result.h" #include "util/similar.h" #include "util/strings.h" namespace bpftrace::ast { using bpftrace::stdlib::Stdlib; class ResolveImports : public Visitor { public: ResolveImports(Imports &imports, const std::vector &paths = {}) : imports_(imports), paths_(paths) {}; using Visitor::visit; void visit(Import &imp); private: Imports &imports_; const std::vector &paths_; }; static bool check_permissions(const std::filesystem::path &path) { auto check_one = [](const std::filesystem::path &path) { auto status = std::filesystem::status(path); auto permissions = status.permissions(); return (permissions & std::filesystem::perms::others_write) == std::filesystem::perms::none; }; if (!check_one(path)) { return false; } if (path.parent_path().empty()) { return check_one("."); } else { return check_one(path.parent_path()); } } static Result import_script([[maybe_unused]] Node &node, Imports &imports, const std::string &name, const std::string &&data, const std::vector &paths, std::map &contents, bool internal) { if (contents.contains(name)) { return OK(); // Already added. } // Construct our context. auto [it, added] = contents.emplace( name, ScriptObject(ASTContext(name, data), internal)); assert(added); auto &ast = it->second.ast; // Perform the basic parse pass. Note that this parse is done extremely // early, and does zero expansion or parsing of attachpoints, etc. PassManager pm; pm.put(ast); pm.add(CreateParsePass()); // Attempt to parse the source. auto ok = pm.run(); if (!ok) { return ok.takeError(); } // Disallow `config` blocks as they cannot be merged. if (ast.root != nullptr && ast.root->config != nullptr && !ast.root->config->stmts.empty()) { ast.root->config->addError() << "invalid `config` within import"; } // Recursively visit the parsed tree. ResolveImports resolver(imports, paths); resolver.visit(ast.root); return OK(); } static Result import_script(Node &node, Imports &imports, const std::string &name, const std::filesystem::path &path, const std::vector &paths, std::map &contents) { if (contents.contains(name)) { return OK(); // Already added. } // Load the file. std::ifstream file(path); if (file.fail()) { node.addError() << "error reading import '" << path << "': " << std::strerror(errno); return OK(); } std::stringstream buf; buf << file.rdbuf(); return import_script(node, imports, name, buf.str(), paths, contents, false); } static Result import_object(Node &node, const std::string &name, const std::filesystem::path &path, std::map &contents) { if (contents.contains(name)) { return OK(); // Already added. } auto added = contents.emplace(name, ExternalObject(node, path)).second; assert(added); return OK(); } static Result import_c(Node &node, const std::string &name, const std::filesystem::path &path, std::map &contents) { if (contents.contains(name)) { return OK(); // Already added. } // Load the file. std::ifstream file(path); if (file.fail()) { node.addError() << "error reading import '" << path << "': " << std::strerror(errno); return OK(); } std::stringstream buf; buf << file.rdbuf(); auto [_, added] = contents.emplace(name, LoadedObject(node, buf.str())); assert(added); return OK(); } static Result import_c(Node &node, const std::string &name, const std::string_view &data, std::map &contents) { if (contents.contains(name)) { return OK(); // Already added. } auto [_, added] = contents.emplace(name, LoadedObject(node, data)); assert(added); return OK(); } Result Imports::import_any(Node &node, const std::string &name, const std::filesystem::path &path, const std::vector &paths, bool ignore_unknown, bool allow_directories) { if (!check_permissions(path)) { node.addError() << "cowardly refusing to import from a directory with " "global write permissions: " << path; return OK(); } if (std::filesystem::is_directory(path)) { // If `recurse` is not set, just ignore this directory. if (!allow_directories) { return OK(); } // Recursively import all entries in the directory. Note that the directory // iterator will never include '.' or '..' entries. for (const auto &entry : std::filesystem::directory_iterator(path)) { auto ok = import_any(node, name + "/" + entry.path().filename().string(), entry.path(), paths, true, false); if (!ok) { return ok.takeError(); } } return OK(); } else { // Import any support file-based extensions. if (path.extension() == ".bt") { return import_script(node, *this, name, path, paths, scripts); } else if (path.extension() == ".c" && path.stem().extension() == ".bpf") { return import_c(node, name, path, c_sources); } else if (path.extension() == ".h") { return import_c(node, name, path, c_headers); } else if (path.extension() == ".o" && path.stem().extension() == ".bpf") { return import_object(node, name, path, objects); } else if (!ignore_unknown) { node.addError() << "unknown import type: " << path.filename(); } return OK(); } } Result Imports::import_any(Node &node, const std::string &name, const std::string_view &data, const std::vector &paths, bool ignore_unknown) { // Import supported extensions. std::filesystem::path path(name); if (path.extension() == ".bt") { return import_script( node, *this, name, std::string(data), paths, scripts, true); } else if (path.extension() == ".c" && path.stem().extension() == ".bpf") { return import_c(node, name, data, c_sources); } else if (path.extension() == ".h") { return import_c(node, name, data, c_headers); } else if (!ignore_unknown) { node.addError() << "unknown import type: " << path; } return OK(); } Result Imports::import_any(Node &node, const std::string &name, const std::vector &paths) { // Prevent direct re-importation of the same top-level name. This is used // because the name may match against directories or internal packages which // contain different files. if (packages_.contains(name)) { return OK(); } packages_.emplace(name); std::vector checked; for (const auto &import_path : paths) { // Check to see if this specific file exists. auto path = import_path / name; checked.emplace_back(path.string()); std::error_code ec; if (!std::filesystem::exists(path, ec)) { continue; // No file found. } // For loading anything from the filesystem, ensure that the name import // is not inside a globally writable directory. Note that we log this as // a warning for a search, which is different than the case where the // path is imported explicitly. if (!check_permissions(path)) { node.addWarning() << "skipping due to global write permissions: " << path; continue; } // Attempt the import. return import_any(node, name, path, paths, false, true); } // See if this matches a set of builtins. Note that we do the "directory" // expansion here, importing anything that is matching as a path. bool found = false; std::vector similar; for (const auto &[internal_path, s] : Stdlib::files) { auto path = std::filesystem::path(internal_path); if (path.string() == name) { return import_any(node, name, s, paths, false); } else if (path.parent_path().string() == name) { auto ok = import_any(node, path.string(), s, paths, true); if (!ok) { return ok.takeError(); } // As long as we found at least one import that matched the standard // library paths, then we call it a victory. found = true; } if (util::is_similar(name, internal_path)) { similar.push_back(internal_path); } } if (found) { return OK(); } // Unable to find a suitable import. auto &err = node.addError(); err << "Unable to find suitable path for import"; if (!checked.empty()) { err.addHint() << "checked: " << util::str_join(checked, ","); } if (!similar.empty()) { err.addHint() << "similar to builtins: " << util::str_join(similar, ","); } return OK(); } void ResolveImports::visit(Import &imp) { auto ok = imports_.import_any(imp, imp.name, paths_); if (!ok) { imp.addError() << "import error: " << ok.takeError(); } } Pass CreateResolveImportsPass(std::vector &&import_paths) { return Pass::create("ResolveImports", [import_paths](ASTContext &ast) -> Result { Imports imports; // Add the source location as a primary path. const auto &filename = ast.source()->filename; std::vector updated_paths; updated_paths.emplace_back( std::filesystem::path(filename).parent_path()); // Add all additional paths. for (const auto &path : import_paths) { updated_paths.emplace_back(path); } // Resolve all imports. ResolveImports analyser(imports, updated_paths); analyser.visit(ast.root); // Ensure that the standard library is imported. The // implicit import is only permitted from the embedded // standard library. Overriding this is possible, but // it must be explicitly imported. auto ok = imports.import_any(*ast.root, "stdlib"); if (!ok) { return ok.takeError(); } // Return all calculated imports. return imports; }); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/resolve_imports.h000066400000000000000000000064211506776124200216620ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include "ast/context.h" #include "ast/pass_manager.h" namespace bpftrace::ast { // LoadedObject is a generic wrapper around some data that was either embedded, // or has been loaded off the filesystem. We no longer depend on any files. class LoadedObject { public: LoadedObject(Node &node, const std::string_view &data) : node(node), data_(data) {}; LoadedObject(Node &node, std::string &&data) : node(node), data_(std::move(data)) {}; std::string_view data() { return std::visit([](const auto &v) -> std::string_view { return v; }, data_); } // Original node, for errors. Node &node; private: // The reason for this extra indirection: the data is either owned, // or it will be a reference to something immutable in the binary. const std::variant data_; }; class ExternalObject { public: ExternalObject(Node &node, std::filesystem::path path) : node(node), path(std::move(path)) {}; // Per above, the original node. Node &node; // Objects are left on the filesystem, since these paths are passed directly // to the linker. const std::filesystem::path path; }; class ScriptObject { public: ScriptObject(ASTContext &&ast, bool internal) : ast(std::move(ast)), internal(internal) {}; // The parsed source context. ASTContext ast; // Indicates whether or not this came from an internal source (e.g. the // standard library). This may be used by subsequent passes to import/resolve // in different orders or at different times. const bool internal; }; // Imports holds the set of imported modules. This includes the parsed ASTs for // any source files, the loaded module for serialized modules, any dlhandles // for loaded dynamic plugins, and the BPF objects for any binary blobs. class Imports : public ast::State<"imports"> { public: std::map c_sources; std::map c_headers; std::map objects; std::map scripts; // Public import call. Result import_any(Node &node, const std::string &name, const std::vector &paths = {}); private: Result import_any(Node &node, const std::string &name, const std::filesystem::path &path, const std::vector &paths, bool ignore_unknown, bool allow_directories); Result import_any(Node &node, const std::string &name, const std::string_view &data, const std::vector &paths, bool ignore_unknown); // Record full packages/directories that have been imported separately from // the specific modules, in order to avoid re-importing these paths. std::unordered_set packages_; }; // This pass resolves imports from the AST itself. Pass CreateResolveImportsPass(std::vector &&import_paths = {}); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/resource_analyser.cpp000066400000000000000000000520371506776124200225120ustar00rootroot00000000000000 #include #include "ast/async_event_types.h" #include "ast/codegen_helper.h" #include "ast/passes/map_sugar.h" #include "ast/passes/named_param.h" #include "ast/passes/resource_analyser.h" #include "ast/visitor.h" #include "bpftrace.h" #include "log.h" #include "required_resources.h" #include "struct.h" #include "types.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace::ast { namespace { // 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(BPFtrace &bpftrace, MapMetadata &mm, NamedParamDefaults &named_param_defaults); using Visitor::visit; void visit(Probe &probe); void visit(Subprog &subprog); void visit(Builtin &builtin); void visit(Call &call); void visit(Map &map); void visit(MapAccess &acc); void visit(MapDeclStatement &decl); void visit(Tuple &tuple); void visit(For &f); void visit(Ternary &ternary); void visit(AssignMapStatement &assignment); void visit(AssignVarStatement &assignment); void visit(VarDeclStatement &decl); // This will move the compute resources value, it should be called only // after the top-level visit. RequiredResources resources(); 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, const Expression &key_expr); void update_map_info(Map &map); void update_variable_info(Variable &var); RequiredResources resources_; BPFtrace &bpftrace_; MapMetadata map_metadata_; NamedParamDefaults &named_param_defaults_; // Current probe we're analysing Probe *probe_{ nullptr }; std::unordered_map> map_decls_; int next_map_id_ = 0; }; } // 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. static ProbeType single_provider_type_postsema(Probe *probe) { if (!probe->attach_points.empty()) { return probetype(probe->attach_points.at(0)->provider); } return ProbeType::invalid; } ResourceAnalyser::ResourceAnalyser(BPFtrace &bpftrace, MapMetadata &mm, NamedParamDefaults &named_param_defaults) : bpftrace_(bpftrace), map_metadata_(mm), named_param_defaults_(named_param_defaults) { } RequiredResources ResourceAnalyser::resources() { if (resources_.max_fmtstring_args_size > 0) { resources_.global_vars.add_known(bpftrace::globalvars::FMT_STRINGS_BUFFER); } if (resources_.max_tuple_size > 0) { assert(resources_.tuple_buffers > 0); resources_.global_vars.add_known(bpftrace::globalvars::TUPLE_BUFFER); } if (resources_.str_buffers > 0) { resources_.global_vars.add_known(bpftrace::globalvars::GET_STR_BUFFER); } if (resources_.max_read_map_value_size > 0) { assert(resources_.read_map_value_buffers > 0); resources_.global_vars.add_known( bpftrace::globalvars::READ_MAP_VALUE_BUFFER); } if (resources_.max_write_map_value_size > 0) { resources_.global_vars.add_known( bpftrace::globalvars::WRITE_MAP_VALUE_BUFFER); } if (resources_.max_variable_size > 0) { assert(resources_.variable_buffers > 0); resources_.global_vars.add_known(bpftrace::globalvars::VARIABLE_BUFFER); } if (resources_.max_map_key_size > 0) { assert(resources_.map_key_buffers > 0); resources_.global_vars.add_known(bpftrace::globalvars::MAP_KEY_BUFFER); } resources_.global_vars.add_known(bpftrace::globalvars::MAX_CPU_ID); resources_.global_vars.add_known(bpftrace::globalvars::EVENT_LOSS_COUNTER); return 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_); } else if (builtin.ident == "__builtin_ncpus") { resources_.global_vars.add_known(bpftrace::globalvars::NUM_CPUS); } } void ResourceAnalyser::visit(Call &call) { Visitor::visit(call); if (call.func == "printf" || call.func == "errorf" || call.func == "system" || call.func == "cat" || call.func == "debugf") { std::vector args; for (auto it = call.vargs.begin() + 1; it != call.vargs.end(); it++) { args.push_back(it->type()); } // 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); // 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. Note that since this // will end up being aligned differently than the arguments themselves, // we need to ensure that it is aligned to the size of the largest type. uint64_t args_size = sizeof(uint64_t) + static_cast(tuple->size); if (args_size % sizeof(uint64_t) != 0) { args_size += sizeof(uint64_t) - (args_size % sizeof(uint64_t)); } if (exceeds_stack_limit(args_size)) { resources_.max_fmtstring_args_size = std::max( resources_.max_fmtstring_args_size, args_size); } auto fmtstr = call.vargs.at(0).as()->value; if (call.func == "printf") { if (probe_ != nullptr && single_provider_type_postsema(probe_) == ProbeType::iter) { resources_.bpf_print_fmts.emplace_back(fmtstr); } else { resources_.printf_args.emplace_back( fmtstr, tuple->fields, PrintfSeverity::NONE, SourceInfo(call.loc)); } } else if (call.func == "errorf") { resources_.printf_args.emplace_back( fmtstr, tuple->fields, PrintfSeverity::ERROR, SourceInfo(call.loc)); } else if (call.func == "debugf") { resources_.bpf_print_fmts.emplace_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 ? call.vargs.at(1).as()->value : " "; resources_.join_args.push_back(delim); } else if (call.func == "count" || call.func == "sum" || call.func == "min" || call.func == "max" || call.func == "avg") { resources_.global_vars.add_known(bpftrace::globalvars::NUM_CPUS); } else if (call.func == "hist") { Map *map = call.vargs.at(0).as(); uint64_t bits = call.vargs.at(3).as()->value; auto args = HistogramArgs{ .bits = static_cast(bits), }; auto &map_info = resources_.maps_info[map->ident]; if (std::holds_alternative(map_info.detail)) { map_info.detail.emplace(args); } else if (std::holds_alternative(map_info.detail) && std::get(map_info.detail) == args) { // Same arguments. } else { call.addError() << "Different bits in a single hist unsupported"; } } else if (call.func == "lhist") { Map *map = call.vargs.at(0).as(); Expression &min_arg = call.vargs.at(3); Expression &max_arg = call.vargs.at(4); Expression &step_arg = call.vargs.at(5); auto &min = *min_arg.as(); auto &max = *max_arg.as(); auto &step = *step_arg.as(); auto args = LinearHistogramArgs{ .min = static_cast(min.value), .max = static_cast(max.value), .step = static_cast(step.value), }; auto &map_info = resources_.maps_info[map->ident]; if (std::holds_alternative(map_info.detail)) { map_info.detail.emplace(args); } else if (std::holds_alternative(map_info.detail) && std::get(map_info.detail) == args) { // Same arguments. } else { call.addError() << "Different lhist bounds in a single map unsupported"; } } else if (call.func == "tseries") { Map *map = call.vargs.at(0).as(); Expression &n_arg = call.vargs.at(2); Expression &interval_ns_arg = call.vargs.at(3); Expression &num_intervals_arg = call.vargs.at(4); auto &interval_ns = *interval_ns_arg.as(); auto &num_intervals = *num_intervals_arg.as(); auto args = TSeriesArgs{ .interval_ns = static_cast(interval_ns.value), .num_intervals = static_cast(num_intervals.value), .value_type = n_arg.type(), .agg = TSeriesAggFunc::none, }; if (call.vargs.size() == 6) { auto &agg_func = *call.vargs.at(5).as(); if (agg_func.value == "avg") { args.agg = TSeriesAggFunc::avg; } else if (agg_func.value == "max") { args.agg = TSeriesAggFunc::max; } else if (agg_func.value == "min") { args.agg = TSeriesAggFunc::min; } else if (agg_func.value == "sum") { args.agg = TSeriesAggFunc::sum; } } auto &map_info = resources_.maps_info[map->ident]; if (std::holds_alternative(map_info.detail)) { map_info.detail.emplace(args); } else if (std::holds_alternative(map_info.detail) && std::get(map_info.detail) == args) { // Same arguments. } else { call.addError() << "Different tseries bounds in a single map unsupported"; } } else if (call.func == "time") { if (!call.vargs.empty()) resources_.time_args.push_back(call.vargs.at(0).as()->value); else resources_.time_args.emplace_back("%H:%M:%S\n"); } else if (call.func == "strftime") { resources_.strftime_args.push_back(call.vargs.at(0).as()->value); } else if (call.func == "print") { constexpr auto nonmap_headroom = sizeof(AsyncEvent::PrintNonMap); auto &arg = call.vargs.at(0); if (!arg.is()) { 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 if (call.func == "cgroup_path") { if (call.vargs.size() > 1) resources_.cgroup_path_args.push_back( call.vargs.at(1).as()->value); else resources_.cgroup_path_args.emplace_back("*"); } else if (call.func == "skboutput") { const auto &file = call.vargs.at(0).as()->value; const auto &offset = call.vargs.at(3).as()->value; resources_.skboutput_args_.emplace_back(file, offset); resources_.using_skboutput = true; } else if (call.func == "delete") { auto &arg0 = call.vargs.at(0); auto &map = *arg0.as(); if (exceeds_stack_limit(map.value_type.GetSize())) { resources_.max_write_map_value_size = std::max( resources_.max_write_map_value_size, map.value_type.GetSize()); } } if (call.func == "print" || call.func == "clear" || call.func == "zero") { if (auto *map = call.vargs.at(0).as()) { auto &name = map->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_->max_strlen; if (exceeds_stack_limit(max_strlen)) resources_.str_buffers++; } // These functions, some of which are desugared AssignMapStatements (e.g., // `@a[1, 2, 3] = count(); -> count(@a, (1, 2, 3));`) might require // additional map key scratch buffers because the map key type might be // a slightly different type due to type promotion in an earlier pass. // This requires us to allocate a new map key (or create a scratch buffer) // and copy individual elements of the tuple instead of the whole thing. if (getAssignRewriteFuncs().contains(call.func) || call.func == "delete" || call.func == "has_key") { if (call.func == "lhist" || call.func == "hist" || call.func == "tseries") { auto &map = *call.vargs.at(0).as(); // Allocation is always needed for lhist/hist/tseries but we need to // allocate space for both map key and the bucket ID from a call to // linear/log2/tseries functions. const auto map_key_size = map.key_type.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 { maybe_allocate_map_key_buffer(*call.vargs.at(0).as(), call.vargs.at(1)); } } 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(MapDeclStatement &decl) { Visitor::visit(decl); auto bpf_type = get_bpf_map_type(decl.bpf_type); if (!bpf_type) { LOG(BUG) << "No bpf type from string: " << decl.bpf_type; return; } map_decls_.insert({ decl.ident, { *bpf_type, decl.max_entries } }); } void ResourceAnalyser::visit(Map &map) { Visitor::visit(map); auto it = named_param_defaults_.defaults.find(map.ident); if (it != named_param_defaults_.defaults.end()) { resources_.global_vars.add_named_param(map.ident, it->second); if (std::holds_alternative(it->second)) { const auto max_strlen = bpftrace_.config_->max_strlen; if (exceeds_stack_limit(max_strlen)) resources_.str_buffers++; } return; } update_map_info(map); } void ResourceAnalyser::visit(MapAccess &acc) { visit(acc.map); visit(acc.key); if (exceeds_stack_limit(acc.type().GetSize())) { resources_.read_map_value_buffers++; resources_.max_read_map_value_size = std::max( resources_.max_read_map_value_size, acc.type().GetSize()); } maybe_allocate_map_key_buffer(*acc.map, acc.key); } void ResourceAnalyser::visit(Tuple &tuple) { Visitor::visit(tuple); if (exceeds_stack_limit(tuple.tuple_type.GetSize())) { resources_.tuple_buffers++; resources_.max_tuple_size = std::max(resources_.max_tuple_size, tuple.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.map); visit(assignment.key); visit(assignment.expr); // The `MapAccess` validated the read limit, we know this to be // a write, so we validate the write limit. if (needAssignMapStatementAllocation(assignment)) { if (exceeds_stack_limit(assignment.map->value_type.GetSize())) { resources_.max_write_map_value_size = std::max( resources_.max_write_map_value_size, assignment.map->value_type.GetSize()); } } maybe_allocate_map_key_buffer(*assignment.map, assignment.key); } 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.result_type.IsStringTy()) { const auto max_strlen = bpftrace_.config_->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.var_type.GetSize())) { resources_.variable_buffers++; resources_.max_variable_size = std::max(resources_.max_variable_size, var.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_->on_stack_limit; } bool ResourceAnalyser::uses_usym_table(const std::string &fun) { return fun == "usym" || fun == "__builtin_func" || fun == "ustack"; } void ResourceAnalyser::update_map_info(Map &map) { auto &map_info = resources_.maps_info[map.ident]; map_info.value_type = map.value_type; map_info.key_type = map.key_type; map_info.is_scalar = map_metadata_.scalar[map.ident]; auto decl = map_decls_.find(map.ident); if (decl != map_decls_.end()) { map_info.bpf_type = decl->second.first; map_info.max_entries = decl->second.second; } else { map_info.bpf_type = get_bpf_map_type(map_info.value_type, map_info.is_scalar); // 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 (!map.type().IsMultiKeyMapTy() && map_info.is_scalar) { map_info.max_entries = 1; } else { map_info.max_entries = bpftrace_.config_->max_map_keys; } } } void ResourceAnalyser::maybe_allocate_map_key_buffer(const Map &map, const Expression &key_expr) { const auto map_key_size = map.key_type.GetSize(); if (needMapKeyAllocation(map, key_expr) && 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 = [](ASTContext &ast, BPFtrace &b, MapMetadata &mm, NamedParamDefaults &named_param_defaults) { ResourceAnalyser analyser(b, mm, named_param_defaults); analyser.visit(ast.root); b.resources = analyser.resources(); }; return Pass::create("ResourceAnalyser", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/resource_analyser.h000066400000000000000000000002011506776124200221410ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateResourcePass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/return_path_analyser.cpp000066400000000000000000000033161506776124200232120ustar00rootroot00000000000000#include #include "ast/passes/return_path_analyser.h" #include "ast/visitor.h" namespace bpftrace::ast { namespace { class ReturnPathAnalyser : public Visitor { public: // visit methods return true iff all return paths of the analyzed code // (represented by the given node) return a value. using Visitor::visit; bool visit(Program &prog); bool visit(Subprog &subprog); bool visit(Jump &jump); bool visit(If &if_node); }; } // namespace bool ReturnPathAnalyser::visit(Program &prog) { return std::ranges::all_of(prog.functions, [this](auto *subprog) { return visit(*subprog); }); } bool ReturnPathAnalyser::visit(Subprog &subprog) { if (subprog.return_type.IsVoidTy()) return true; for (auto &stmt : subprog.stmts) { if (visit(stmt)) return true; } subprog.addError() << "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 (auto &stmt : if_node.if_block->stmts) { if (visit(stmt)) result = true; } if (!result) { // if block has no return return false; } // True if both blocks have a return. // False if else block has no return (or there is no else block). return std::ranges::any_of(if_node.else_block->stmts, [this](auto &stmt) { return visit(stmt); }); } Pass CreateReturnPathPass() { auto fn = [](ASTContext &ast) { ReturnPathAnalyser return_path; return_path.visit(ast.root); }; return Pass::create("ReturnPath", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/return_path_analyser.h000066400000000000000000000002031506776124200226470ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateReturnPathPass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/semantic_analyser.cpp000066400000000000000000004733731506776124200225000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "arch/arch.h" #include "ast/ast.h" #include "ast/async_event_types.h" #include "ast/context.h" #include "ast/helpers.h" #include "ast/passes/fold_literals.h" #include "ast/passes/map_sugar.h" #include "ast/passes/named_param.h" #include "ast/passes/semantic_analyser.h" #include "ast/passes/type_system.h" #include "ast/signal_bt.h" #include "btf/compat.h" #include "collect_nodes.h" #include "config.h" #include "log.h" #include "probe_matcher.h" #include "tracepoint_format_parser.h" #include "types.h" #include "usdt.h" #include "util/paths.h" #include "util/strings.h" #include "util/system.h" #include "util/wildcard.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace::ast { namespace { 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; }; struct arg_type_spec { Type type = Type::integer; bool literal = false; // This indicates that this is just a placeholder as we use the index in the // vector of arg_type_spec as the number argument to check. bool skip_check = false; }; struct map_type_spec { // This indicates that the argument must be a map type. The given function // may be called to determine the map type. std::function type; }; struct map_key_spec { // This indicates that the argument is a key expression for another map // argument, which is found in argument `map_index`. size_t map_index; }; struct call_spec { size_t min_args = 0; size_t max_args = 0; bool discard_ret_warn = false; // NOLINTBEGIN(readability-redundant-member-init) std::vector> arg_types = {}; // NOLINTEND(readability-redundant-member-init) }; class SemanticAnalyser : public Visitor { public: explicit SemanticAnalyser(ASTContext &ctx, BPFtrace &bpftrace, CDefinitions &c_definitions, MapMetadata &map_metadata, NamedParamDefaults &named_param_defaults, TypeMetadata &type_metadata, bool has_child = true, bool listing = false) : ctx_(ctx), bpftrace_(bpftrace), c_definitions_(c_definitions), map_metadata_(map_metadata), named_param_defaults_(named_param_defaults), type_metadata_(type_metadata), listing_(listing), has_child_(has_child) { } int analyse(); using Visitor::visit; 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(MapAddr &map_addr); void visit(MapDeclStatement &decl); void visit(Variable &var); void visit(VariableAddr &var_addr); 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(TupleAccess &acc); void visit(MapAccess &acc); void visit(Cast &cast); void visit(Tuple &tuple); void visit(Expression &expr); void visit(ExprStatement &expr); void visit(AssignMapStatement &assignment); void visit(AssignVarStatement &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(Block &block); void visit(BlockExpr &block_expr); void visit(Subprog &subprog); private: ASTContext &ctx_; PassTracker pass_tracker_; BPFtrace &bpftrace_; CDefinitions &c_definitions_; MapMetadata &map_metadata_; NamedParamDefaults &named_param_defaults_; TypeMetadata &type_metadata_; bool listing_; bool is_final_pass() const; bool is_first_pass() const; std::optional check(Sizeof &szof); std::optional check(Offsetof &offof); [[nodiscard]] bool check_arg(const Call &call, size_t index, const arg_type_spec &spec); [[nodiscard]] bool check_arg(const Call &call, size_t index, const map_type_spec &spec); [[nodiscard]] bool check_arg(const Call &call, size_t index, const map_key_spec &spec); [[nodiscard]] bool check_call(const Call &call); [[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, size_t index, bool want_literal = false); bool check_symbol(const Call &call, int arg_num); void check_stack_call(Call &call, bool kernel); Probe *get_probe(Node &node, std::string name = ""); bool is_valid_assignment(const Expression &expr, bool map_without_type); SizedType *get_map_type(const Map &map); SizedType *get_map_key_type(const Map &map); void assign_map_type(Map &map, const SizedType &type, const Node *loc_node, AssignMapStatement *assignment = nullptr); SizedType create_key_type(const SizedType &expr_type, Node &node); void reconcile_map_key(Map *map, const Expression &key_expr); 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 Expression &key_expr); bool update_string_size(SizedType &type, const SizedType &new_type); SizedType create_merged_tuple(const SizedType &left, const SizedType &right); void validate_map_key(const SizedType &key, Node &node); void resolve_struct_type(SizedType &type, Node &node); 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); bool has_error() const; bool in_loop() { 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); void check_variable(Variable &var, bool check_assigned); 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_; std::map bpf_map_type_; uint32_t loop_depth_ = 0; bool has_begin_probe_ = false; bool has_end_probe_ = false; bool has_child_ = false; std::unordered_map benchmark_locs_; }; } // namespace static const std::map CALL_SPEC = { { "avg", { .min_args = 3, .max_args = 3, .arg_types = { map_type_spec{ .type = std::function( [](const ast::Call &call) -> SizedType { return CreateAvg( call.vargs.at(2).type().IsSigned()); }) }, map_key_spec{ .map_index = 0 }, arg_type_spec{ .type = Type::integer } } } }, { "bswap", { .min_args = 1, .max_args = 1, .discard_ret_warn = true } }, { "buf", { .min_args=1, .max_args=2, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .skip_check=true }, arg_type_spec{ .type=Type::integer } } } }, { "cat", { .min_args=1, .max_args=128, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "cgroupid", { .min_args=1, .max_args=1, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "cgroup_path", { .min_args=1, .max_args=2, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::integer }, arg_type_spec{ .type=Type::string } } } }, { "clear", { .min_args=1, .max_args=1, .arg_types={ map_type_spec{}, } } }, { "count", { .min_args=2, .max_args=2, .arg_types={ map_type_spec{ .type = std::function([](const ast::Call&) -> SizedType { return CreateCount(); }) }, map_key_spec{ .map_index=0 }, } } }, { "debugf", { .min_args=1, .max_args=128, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "delete", { .min_args=2, .max_args=2, .arg_types={ map_type_spec{}, map_key_spec{ .map_index=0 }, } } }, { "errorf", { .min_args=1, .max_args=128, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "exit", { .min_args=0, .max_args=1, .arg_types={ arg_type_spec{ .type=Type::integer } } } }, { "has_key", { .min_args=2, .max_args=2, .discard_ret_warn = true, .arg_types={ map_type_spec{}, map_key_spec{ .map_index=0 }, } } }, { "hist", { .min_args=3, .max_args=4, .arg_types={ map_type_spec{ .type = std::function([]([[maybe_unused]] const ast::Call &call) -> SizedType { return CreateHist(); }) }, map_key_spec{ .map_index=0 }, arg_type_spec{ .type=Type::integer }, arg_type_spec{ .type=Type::integer, .literal=true } } } }, { "join", { .min_args=1, .max_args=2, .arg_types={ arg_type_spec{ .skip_check=true }, arg_type_spec{ .type=Type::string, .literal=true } } } }, { "kaddr", { .min_args=1, .max_args=1, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "kptr", { .min_args=1, .max_args=1, .discard_ret_warn = true, } }, { "kstack", { .min_args=0, .max_args=2, .discard_ret_warn = true, } }, { "ksym", { .min_args=1, .max_args=1, .discard_ret_warn = true, } }, { "len", { .min_args=1, .max_args=1, .discard_ret_warn = true, // This cannot be checked without overloading: it requires *either* a // stack type, or a non-scalar map. } }, { "lhist", { .min_args=6, .max_args=6, .arg_types={ map_type_spec{ .type = std::function([]([[maybe_unused]] const ast::Call &call) -> SizedType { return CreateLhist(); }) }, map_key_spec{ .map_index=0 }, arg_type_spec{ .type=Type::integer }, arg_type_spec{ .type=Type::integer, .literal=true }, arg_type_spec{ .type=Type::integer, .literal=true }, arg_type_spec{ .type=Type::integer, .literal=true } } } }, { "tseries", { .min_args=5, .max_args=6, .arg_types={ map_type_spec{ .type = std::function([]([[maybe_unused]] const ast::Call &call) -> SizedType { return CreateTSeries(); }) }, map_key_spec{ .map_index=0 }, arg_type_spec{ .type=Type::integer }, arg_type_spec{ .type=Type::integer, .literal=true }, arg_type_spec{ .type=Type::integer, .literal=true }, arg_type_spec{ .type=Type::string, .literal=true } } } }, { "macaddr", { .min_args=1, .max_args=1, .discard_ret_warn = true, } }, { "max", { .min_args=3, .max_args=3, .arg_types={ map_type_spec{ .type = std::function([](const ast::Call &call) -> SizedType { return CreateMax(call.vargs.at(2).type().IsSigned()); }) }, map_key_spec{ .map_index=0 }, arg_type_spec{ .type=Type::integer } } } }, { "min", { .min_args=3, .max_args=3, .arg_types={ map_type_spec{ .type = std::function([](const ast::Call &call) -> SizedType { return CreateMin(call.vargs.at(2).type().IsSigned()); }) }, map_key_spec{ .map_index=0 }, arg_type_spec{ .type=Type::integer } } } }, { "nsecs", { .min_args=0, .max_args=1, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::timestamp_mode } } } }, { "ntop", { .min_args=1, .max_args=2, .discard_ret_warn = true, } }, { "offsetof", { .min_args=2, .max_args=2, .discard_ret_warn = true, } }, { "override", { .min_args=1, .max_args=1, .arg_types={ arg_type_spec{ .type=Type::integer } } } }, { "path", { .min_args=1, .max_args=2, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .skip_check=true }, arg_type_spec{ .type=Type::integer, .literal=true } } } }, { "percpu_kaddr", { .min_args=1, .max_args=2, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true }, arg_type_spec{ .type=Type::integer } } } }, { "print", { .min_args=1, .max_args=3, .arg_types={ // This may be a pure map or not. arg_type_spec{ .skip_check=true }, arg_type_spec{ .type=Type::integer, .literal=true }, arg_type_spec{ .type=Type::integer, .literal=true } } } }, { "printf", { .min_args=1, .max_args=128, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "pton", { .min_args=1, .max_args=1, .discard_ret_warn = true, } }, { "reg", { .min_args=1, .max_args=1, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "signal", { .min_args=1, .max_args=1, } }, { "sizeof", { .min_args=1, .max_args=1, .discard_ret_warn = true, } }, { "skboutput", { .min_args=4, .max_args=4, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true }, // pcap file name arg_type_spec{ .type=Type::pointer }, // *skb arg_type_spec{ .type=Type::integer }, // cap length // cap offset, default is 0 // some tracepoints like dev_queue_xmit will output ethernet header, // set offset to 14 bytes can exclude this header arg_type_spec{ .type=Type::integer } } } }, { "stats", { .min_args=3, .max_args=3, .arg_types={ map_type_spec{ .type = std::function([](const ast::Call &call) -> SizedType { return CreateStats(call.vargs.at(2).type().IsSigned()); }) }, map_key_spec{ .map_index=0 }, arg_type_spec{ .type=Type::integer } } } }, { "str", { .min_args=1, .max_args=2, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .skip_check=true }, arg_type_spec{ .type=Type::integer } } } }, { "strerror", { .min_args=1, .max_args=1, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::integer } } } }, { "strftime", { .min_args=2, .max_args=2, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true }, arg_type_spec{ .type=Type::integer } } } }, { "strcontains", { .min_args=2, .max_args=2, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=false }, arg_type_spec{ .type=Type::string, .literal=false } } } }, { "strncmp", { .min_args=3, .max_args=3, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string }, arg_type_spec{ .type=Type::string }, arg_type_spec{ .type=Type::integer, .literal=true } } } }, { "sum", { .min_args=3, .max_args=3, .arg_types={ map_type_spec{ .type = std::function([](const ast::Call &call) -> SizedType { return CreateSum(call.vargs.at(2).type().IsSigned()); }) }, map_key_spec{ .map_index=0 }, arg_type_spec{ .type=Type::integer } } } }, { "system", { .min_args=1, .max_args=128, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "time", { .min_args=0, .max_args=1, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "uaddr", { .min_args=1, .max_args=1, .discard_ret_warn = true, .arg_types={ arg_type_spec{ .type=Type::string, .literal=true } } } }, { "unwatch", { .min_args=1, .max_args=1, .arg_types={ arg_type_spec{ .type=Type::integer } } } }, { "uptr", { .min_args=1, .max_args=1, .discard_ret_warn = true, } }, { "ustack", { .min_args=0, .max_args=2, .discard_ret_warn = true, } }, { "usym", { .min_args=1, .max_args=1, .discard_ret_warn = true, } }, { "zero", { .min_args=1, .max_args=1, .arg_types={ map_type_spec{}, } } }, { "pid", { .min_args=0, .max_args=1, .discard_ret_warn = true }, }, { "socket_cookie", { .min_args=1, .max_args=1, .arg_types={ arg_type_spec{ .type=Type::pointer }, // struct sock * } } }, }; // clang-format on 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::tseries_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::timestamp_mode: case Type::boolean: 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 &expr, bool map_without_type) { // Prevent assigning aggregations to another map. if (expr.type().IsMultiKeyMapTy()) { return false; } else if (expr.type().NeedsPercpuMap() && !expr.type().IsCastableMapTy()) { return false; } else if (expr.type().IsCastableMapTy() && map_without_type) { return false; } else if (is_final_pass() && expr.type().IsNoneTy()) { return false; } return true; } void SemanticAnalyser::visit(String &string) { // 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_ == "errorf") && func_arg_idx_ == 0) return; const auto str_len = bpftrace_.config_->max_strlen; if (!is_compile_time_func(func_) && string.value.size() > str_len - 1) { string.addError() << "String is too long (over " << str_len << " bytes): " << string.value; } // @a = buf("hi", 2). String allocated on bpf stack. See codegen string.string_type.SetAS(AddrSpace::kernel); } void SemanticAnalyser::visit(Identifier &identifier) { if (c_definitions_.enums.contains(identifier.ident)) { const auto &enum_name = std::get<1>(c_definitions_.enums[identifier.ident]); identifier.ident_type = CreateEnum(64, enum_name); } else if (bpftrace_.structs.Has(identifier.ident)) { identifier.ident_type = CreateRecord( identifier.ident, bpftrace_.structs.Lookup(identifier.ident)); } else if (func_ == "sizeof" && getIntcasts().contains(identifier.ident)) { identifier.ident_type = CreateInt( std::get<0>(getIntcasts().at(identifier.ident))); } else if (func_ == "nsecs") { identifier.ident_type = CreateTimestampMode(); if (identifier.ident == "monotonic") { identifier.ident_type.ts_mode = TimestampMode::monotonic; } else if (identifier.ident == "boot") { identifier.ident_type.ts_mode = TimestampMode::boot; } else if (identifier.ident == "tai") { identifier.ident_type.ts_mode = TimestampMode::tai; } else if (identifier.ident == "sw_tai") { identifier.ident_type.ts_mode = TimestampMode::sw_tai; } else { identifier.addError() << "Invalid timestamp mode: " << identifier.ident; } } else if (func_ == "pid" || func_ == "tid") { if (identifier.ident != "curr_ns" && identifier.ident != "init") { identifier.addError() << "Invalid PID namespace mode: " << identifier.ident << " (expects: curr_ns or init)"; } } else { // Final attempt: try to parse as a stack mode. ConfigParser parser; StackMode mode; auto ok = parser.parse(func_, &mode, identifier.ident); if (ok) { identifier.ident_type = CreateStack(true, StackType{ .mode = mode }); } else { identifier.addError() << "Unknown identifier: '" + identifier.ident + "'"; } } } void SemanticAnalyser::builtin_args_tracepoint(AttachPoint *attach_point, Builtin &builtin) { std::string tracepoint_struct = TracepointFormatParser::get_struct_name( *attach_point); builtin.builtin_type = CreateRecord( tracepoint_struct, bpftrace_.structs.Lookup(tracepoint_struct)); builtin.builtin_type.SetAS( attach_point->target == "syscalls" ? AddrSpace::user : AddrSpace::kernel); builtin.builtin_type.MarkCtxAccess(); builtin.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::benchmark: 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, 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) builtin.addError() << "ctx cannot be used in different BPF program types: " << progtypeName(bt) << " and " << progtypeName(bt2); } switch (bt) { case libbpf::BPF_PROG_TYPE_KPROBE: { auto record = bpftrace_.structs.Lookup("struct pt_regs"); if (!record.expired()) { builtin.builtin_type = CreatePointer( CreateRecord("struct pt_regs", record), AddrSpace::kernel); builtin.builtin_type.MarkCtxAccess(); } break; } case libbpf::BPF_PROG_TYPE_TRACEPOINT: builtin.addError() << "Use args instead of ctx in tracepoint"; break; case libbpf::BPF_PROG_TYPE_PERF_EVENT: builtin.builtin_type = CreatePointer( CreateRecord("struct bpf_perf_event_data", bpftrace_.structs.Lookup( "struct bpf_perf_event_data")), AddrSpace::kernel); builtin.builtin_type.MarkCtxAccess(); break; case libbpf::BPF_PROG_TYPE_TRACING: if (pt == ProbeType::iter) { std::string type = "struct bpf_iter__" + func; builtin.builtin_type = CreatePointer( CreateRecord(type, bpftrace_.structs.Lookup(type)), AddrSpace::kernel); builtin.builtin_type.MarkCtxAccess(); } else { builtin.addError() << "invalid program type"; } break; default: builtin.addError() << "invalid program type"; break; } } else if (builtin.ident == "pid" || builtin.ident == "tid") { builtin.builtin_type = CreateUInt32(); } else if (builtin.ident == "nsecs" || builtin.ident == "__builtin_elapsed" || builtin.ident == "__builtin_cgroup" || builtin.ident == "__builtin_uid" || builtin.ident == "__builtin_gid" || builtin.ident == "__builtin_cpu" || builtin.ident == "__builtin_rand" || builtin.ident == "__builtin_numaid" || builtin.ident == "__builtin_jiffies" || builtin.ident == "__builtin_ncpus") { builtin.builtin_type = CreateUInt64(); if (builtin.ident == "__builtin_jiffies" && !bpftrace_.feature_->has_helper_jiffies64()) { builtin.addError() << "BPF_FUNC_jiffies64 is not available for your kernel version"; } } else if (builtin.ident == "__builtin_curtask") { // Retype curtask to its original type: struct task_struct. builtin.builtin_type = CreatePointer( CreateRecord("struct task_struct", bpftrace_.structs.Lookup("struct task_struct")), AddrSpace::kernel); } else if (builtin.ident == "__builtin_retval") { auto *probe = get_probe(builtin, builtin.ident); if (probe == nullptr) return; ProbeType type = single_provider_type(probe); if (type == ProbeType::kretprobe || type == ProbeType::uretprobe) { builtin.builtin_type = CreateUInt64(); } else if (type == ProbeType::fentry || type == ProbeType::fexit) { const auto *arg = bpftrace_.structs.GetProbeArg(*probe, RETVAL_FIELD_NAME); if (arg) { builtin.builtin_type = arg->type; } else builtin.addError() << "Can't find a field " << RETVAL_FIELD_NAME; } else { builtin.addError() << "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.builtin_type.SetAS(find_addrspace(type)); } else if (builtin.ident == "kstack") { builtin.builtin_type = CreateStack( true, StackType{ .mode = bpftrace_.config_->stack_mode }); } else if (builtin.ident == "ustack") { builtin.builtin_type = CreateStack( false, StackType{ .mode = bpftrace_.config_->stack_mode }); } else if (builtin.ident == "__builtin_comm") { constexpr int COMM_SIZE = 16; builtin.builtin_type = CreateString(COMM_SIZE); // comm allocated in the bpf stack. See codegen // Case: @=comm and strncmp(@, "name") builtin.builtin_type.SetAS(AddrSpace::kernel); } else if (builtin.ident == "__builtin_func") { auto *probe = get_probe(builtin, 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.builtin_type = CreateKSym(); else if (type == ProbeType::uprobe || type == ProbeType::uretprobe) builtin.builtin_type = CreateUSym(); else if (type == ProbeType::fentry || type == ProbeType::fexit) { if (!bpftrace_.feature_->has_helper_get_func_ip()) { builtin.addError() << "BPF_FUNC_get_func_ip not available for your kernel version"; } builtin.builtin_type = CreateKSym(); } else builtin.addError() << "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()) { builtin.addError() << "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, builtin.ident); if (probe == nullptr) return; ProbeType pt = probetype(probe->attach_points[0]->provider); AddrSpace addrspace = find_addrspace(pt); int arg_num = atoi(builtin.ident.substr(3).c_str()); for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::kprobe && type != ProbeType::uprobe && type != ProbeType::usdt && type != ProbeType::rawtracepoint) builtin.addError() << "The " << builtin.ident << " builtin can only be used with " << "'kprobes', 'uprobes' and 'usdt' probes"; // argx in USDT probes doesn't need to check against arch::max_arg() if (type != ProbeType::usdt && static_cast(arg_num) >= arch::Host::arguments().size()) builtin.addError() << arch::Host::Machine << " doesn't support " << builtin.ident; } builtin.builtin_type = CreateUInt64(); builtin.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, 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) builtin.addError() << "The " + builtin.ident << " builtin can only be used with 'kprobes' and 'uprobes' probes"; 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 builtin.addWarning() << "Using an address offset with the sargX built-in can" "lead to unexpected behavior "; } } builtin.builtin_type = CreateUInt64(); builtin.builtin_type.SetAS(addrspace); } else if (builtin.ident == "__builtin_probe") { auto *probe = get_probe(builtin, builtin.ident); if (probe == nullptr) return; size_t str_size = 0; for (AttachPoint *attach_point : probe->attach_points) { str_size = std::max(str_size, attach_point->name().length()); } builtin.builtin_type = CreateString(str_size + 1); } else if (builtin.ident == "__builtin_username") { builtin.builtin_type = CreateUsername(); } else if (builtin.ident == "__builtin_usermode") { if (arch::Host::Machine != arch::Machine::X86_64) { builtin.addError() << "'usermode' builtin is only supported on x86_64"; return; } builtin.builtin_type = CreateUInt8(); } else if (builtin.ident == "__builtin_cpid") { if (!has_child_) { builtin.addError() << "cpid cannot be used without child command"; } builtin.builtin_type = CreateUInt32(); } else if (builtin.ident == "args") { auto *probe = get_probe(builtin, builtin.ident); if (probe == nullptr) return; for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type == ProbeType::tracepoint) { builtin_args_tracepoint(attach_point, builtin); } } ProbeType type = single_provider_type(probe); if (type == ProbeType::invalid) { builtin.addError() << "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 || type == ProbeType::rawtracepoint) { for (auto *attach_point : probe->attach_points) { if (attach_point->target == "bpf") { builtin.addError() << "The args builtin cannot be used for " "'fentry/fexit:bpf' probes"; return; } } auto type_name = probe->args_typename(); builtin.builtin_type = CreateRecord(type_name, bpftrace_.structs.Lookup(type_name)); if (builtin.builtin_type.GetFieldCount() == 0) builtin.addError() << "Cannot read function parameters"; builtin.builtin_type.MarkCtxAccess(); builtin.builtin_type.is_funcarg = true; builtin.builtin_type.SetAS(type == ProbeType::uprobe ? AddrSpace::user : AddrSpace::kernel); // We'll build uprobe args struct on stack if (type == ProbeType::uprobe) builtin.builtin_type.is_internal = true; } else if (type != ProbeType::tracepoint) // no special action for // tracepoint { builtin.addError() << "The args builtin can only be used with " "tracepoint/fentry/uprobe probes (" << type << " used here)"; } } else if (builtin.ident == "__builtin_session_is_return") { builtin.builtin_type = CreateBool(); } else { builtin.addError() << "Unknown builtin variable: '" << builtin.ident << "'"; } } 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)) { call.addError() << 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) { func_arg_idx_ = i; visit(call.vargs.at(i)); } if (auto *probe = dynamic_cast(top_level_node_)) { for (auto *ap : probe->attach_points) { if (!ap->check_available(call.func)) { call.addError() << call.func << " can not be used with \"" << ap->provider << "\" probes"; } } } if (!check_call(call)) { return; } if (call.func == "hist") { if (call.vargs.size() == 3) { call.vargs.emplace_back( ctx_.make_node(0, Location(call.loc))); // default bits is 0 } else { const auto *bits = call.vargs.at(3).as(); if (!bits) { // Bug here as the validity of the integer literal is already checked by // check_arg above. LOG(BUG) << call.func << ": invalid bits value, need integer literal"; } else if (bits->value > 5) { call.addError() << call.func << ": bits " << bits->value << " must be 0..5"; } } call.return_type = CreateHist(); } else if (call.func == "lhist") { if (is_final_pass()) { Expression &min_arg = call.vargs.at(3); Expression &max_arg = call.vargs.at(4); Expression &step_arg = call.vargs.at(5); auto *min = min_arg.as(); auto *max = max_arg.as(); auto *step = step_arg.as(); if (!min) { call.addError() << call.func << ": invalid min value (must be non-negative literal)"; return; } if (!max) { call.addError() << call.func << ": invalid max value (must be non-negative literal)"; return; } if (!step) { call.addError() << call.func << ": invalid step value"; return; } if (step->value <= 0) { call.addError() << "lhist() step must be >= 1 (" << step->value << " provided)"; } else { int buckets = (max->value - min->value) / step->value; if (buckets > 1000) { call.addError() << "lhist() too many buckets, must be <= 1000 (would need " << buckets << ")"; } } if (min->value > max->value) { call.addError() << "lhist() min must be less than max (provided min " << min->value << " and max " << max->value << ")"; } if ((max->value - min->value) < step->value) { call.addError() << "lhist() step is too large for the given range (provided step " << step->value << " for range " << (max->value - min->value) << ")"; } } } else if (call.func == "tseries") { const static std::set ALLOWED_AGG_FUNCS = { "avg", "sum", "max", "min", }; if (is_final_pass()) { Expression &interval_ns_arg = call.vargs.at(3); Expression &num_intervals_arg = call.vargs.at(4); auto *interval_ns = interval_ns_arg.as(); auto *num_intervals = num_intervals_arg.as(); if (!interval_ns) { call.addError() << call.func << ": invalid interval_ns value (must be non-negative literal)"; return; } if (!num_intervals) { call.addError() << call.func << ": invalid num_intervals value (must be non-negative literal)"; return; } if (interval_ns->value <= 0) { call.addError() << "tseries() interval_ns must be >= 1 (" << interval_ns->value << " provided)"; return; } if (num_intervals->value <= 0) { call.addError() << "tseries() num_intervals must be >= 1 (" << num_intervals->value << " provided)"; return; } else if (num_intervals->value > 1000000) { call.addError() << "tseries() num_intervals must be < 1000000 (" << num_intervals->value << " provided)"; } if (call.vargs.size() == 6) { auto &aggregator = *call.vargs.at(5).as(); if (!ALLOWED_AGG_FUNCS.contains(aggregator.value)) { auto &err = call.addError(); err << "tseries() expects one of the following aggregation " "functions: "; size_t i = 0; for (const std::string &agg : ALLOWED_AGG_FUNCS) { err << agg; if (i++ != ALLOWED_AGG_FUNCS.size() - 1) { err << ", "; } } err << " (\"" << aggregator.value << "\" provided)"; } } } call.return_type = CreateTSeries(); } else if (call.func == "count") { call.return_type = CreateCount(); } else if (call.func == "sum") { call.return_type = CreateSum(call.vargs.at(0).type().IsSigned()); } else if (call.func == "min") { call.return_type = CreateMin(call.vargs.at(0).type().IsSigned()); } else if (call.func == "max") { call.return_type = CreateMax(call.vargs.at(0).type().IsSigned()); } else if (call.func == "avg") { call.return_type = CreateAvg(true); } else if (call.func == "stats") { call.return_type = CreateStats(true); } else if (call.func == "delete") { call.return_type = CreateUInt8(); } else if (call.func == "has_key") { call.return_type = CreateBool(); } else if (call.func == "str") { auto &arg = call.vargs.at(0); const auto &t = arg.type(); if (!t.IsIntegerTy() && !t.IsPtrTy()) { call.addError() << call.func << "() expects an integer or a pointer type as first " << "argument (" << t << " provided)"; } auto strlen = bpftrace_.config_->max_strlen; if (call.vargs.size() == 2) { if (auto *integer = call.vargs.at(1).as()) { if (integer->value + 1 > strlen) { call.addWarning() << "length param (" << integer->value << ") is too long and will be shortened to " << strlen << " bytes (see BPFTRACE_MAX_STRLEN)"; } else { strlen = integer->value + 1; // Storage for NUL byte. } } else if (auto *integer = dynamic_cast( call.vargs.at(1).as())) { call.addError() << call.func << "cannot use negative length (" << integer->value << ")"; } } call.return_type = CreateString(strlen); call.return_type.SetAS(AddrSpace::kernel); } else if (call.func == "buf") { const uint64_t max_strlen = bpftrace_.config_->max_strlen; if (max_strlen > std::numeric_limits::max()) { call.addError() << "BPFTRACE_MAX_STRLEN too large to use on buffer (" << max_strlen << " > " << std::numeric_limits::max() << ")"; } auto &arg = call.vargs.at(0); if (is_final_pass() && !(arg.type().IsIntTy() || arg.type().IsStringTy() || arg.type().IsPtrTy() || arg.type().IsArrayTy())) { call.addError() << 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()) call.addError() << call.func << "() expects a length argument for non-array type " << typestr(arg.type().GetTy()); } else { if (auto *integer = call.vargs.at(1).as()) { buffer_size = integer->value; } else if (auto *integer = call.vargs.at(1).as()) { call.addError() << call.func << "cannot use negative length (" << integer->value << ")"; } } if (buffer_size > max_buffer_size) { if (is_final_pass()) call.addWarning() << 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.return_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.return_type.SetAS(AddrSpace::kernel); } else if (call.func == "ksym" || call.func == "usym") { // allow symbol lookups on casts (eg, function pointers) auto &arg = call.vargs.at(0); const auto &type = arg.type(); if (!type.IsIntegerTy() && !type.IsPtrTy()) call.addError() << call.func << "() expects an integer or pointer argument"; if (call.func == "ksym") call.return_type = CreateKSym(); else if (call.func == "usym") call.return_type = CreateUSym(); } else if (call.func == "ntop") { int index = 0; if (call.vargs.size() == 2) { check_arg(call, Type::integer, 0); index = 1; } auto &arg = call.vargs.at(index); if (!arg.type().IsIntTy() && !arg.type().IsStringTy() && !arg.type().IsArrayTy()) call.addError() << 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) call.addError() << call.func << "() argument must be 4 or 16 bytes in size"; call.return_type = CreateInet(buffer_size); } else if (call.func == "pton") { int af_type = 0, addr_size = 0; std::string addr; if (auto *str = call.vargs.at(0).as()) { addr = str->value; // 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 { call.addError() << call.func << "() expects an string argument of an IPv4/IPv6 address, got " << addr; return; } } else { call.addError() << call.func << "() expects an string literal, got " << call.vargs.at(0).type(); return; } std::vector dst(addr_size); auto ret = inet_pton(af_type, addr.c_str(), dst.data()); if (ret != 1) { call.addError() << call.func << "() expects a valid IPv4/IPv6 address, got " << addr; return; } call.return_type = CreateArray(addr_size, CreateUInt8()); call.return_type.SetAS(AddrSpace::kernel); call.return_type.is_internal = true; } else if (call.func == "join") { call.return_type = CreateNone(); if (!is_final_pass()) return; auto &arg = call.vargs.at(0); if (!(arg.type().IsIntTy() || arg.type().IsPtrTy())) { call.addError() << "() only supports int or pointer arguments" << " (" << arg.type().GetTy() << " provided)"; } } else if (call.func == "reg") { auto reg_name = call.vargs.at(0).as()->value; auto offset = arch::Host::register_to_pt_regs_offset(reg_name); if (!offset) { call.addError() << "'" << reg_name << "' is not a valid register on this architecture" << " (" << arch::Host::Machine << ")"; } call.return_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.return_type.SetAS(find_addrspace(pt)); } else { // Assume kernel space for data in subprogs call.return_type.SetAS(AddrSpace::kernel); } } else if (call.func == "kaddr") { call.return_type = CreateUInt64(); call.return_type.SetAS(AddrSpace::kernel); } else if (call.func == "percpu_kaddr") { const auto &symbol = call.vargs.at(0).as()->value; if (bpftrace_.btf_->get_var_type(symbol).IsNoneTy()) { call.addError() << "Could not resolve variable \"" << symbol << "\" from BTF"; } call.return_type = CreateUInt64(); call.return_type.SetAS(AddrSpace::kernel); } else if (call.func == "uaddr") { auto *probe = get_probe(call, call.func); if (probe == nullptr) return; if (!check_symbol(call, 0)) return; std::vector sizes; auto name = call.vargs.at(0).as()->value; for (auto *ap : probe->attach_points) { struct symbol sym = {}; int err = bpftrace_.resolve_uname(name, &sym, ap->target); if (err < 0 || sym.address == 0) { call.addError() << "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)) { call.addError() << "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.return_type = CreatePointer(CreateInt(pointee_size), AddrSpace::user); } else if (call.func == "cgroupid") { call.return_type = CreateUInt64(); } else if (call.func == "printf" || call.func == "errorf" || call.func == "system" || call.func == "cat" || call.func == "debugf") { if (is_final_pass()) { const auto &fmt = call.vargs.at(0).as()->value; std::vector args; for (size_t i = 1; i < call.vargs.size(); i++) { args.push_back(call.vargs[i].type()); } FormatString fs(fmt); auto ok = fs.check(args); if (!ok) { call.addError() << ok.takeError(); } // The `debugf` call is a builtin, and is subject to more much rigorous // checks. We've already validate the basic counts, etc. but we need to // apply these additional constraints which includes a limited surface. if (call.func == "debugf") { call.addWarning() << "The debugf() builtin is not recommended for production use. " "For more information see bpf_trace_printk in bpf-helpers(7)."; // bpf_trace_printk cannot use more than three arguments, see // bpf-helpers(7). constexpr int PRINTK_MAX_ARGS = 3; if (args.size() > PRINTK_MAX_ARGS) { call.addError() << "cannot use more than " << PRINTK_MAX_ARGS << " conversion specifiers"; } for (size_t i = 0; i < args.size(); i++) { // 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. static const std::unordered_map bpf_trace_printk_format_types = { { "d", Type::integer }, { "u", Type::integer }, { "x", Type::integer }, { "p", Type::integer }, { "s", Type::string } }; auto it = bpf_trace_printk_format_types.find(fs.specs[i].specifier); if (it == bpf_trace_printk_format_types.end()) { call.vargs.at(0).node().addError() << "Invalid format specifier for `debugf`: " << fs.specs[i].specifier; continue; } if (args[i].GetTy() != it->second) { call.vargs.at(i + 1).node().addError() << "Type does not match format specifier: " << fs.specs[i].specifier; continue; } } } } } else if (call.func == "exit") { // Leave as `none`. } else if (call.func == "print") { if (auto *map = call.vargs.at(0).as()) { if (is_final_pass()) { // N.B. that print is parameteric in the type, so this is not checked // by `check_arg` as there is no spec for the first argument. if (map->type().IsNoneTy()) { map->addError() << "Undefined map: " + map->ident; } else { if (in_loop()) { call.addWarning() << "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 (map->value_type.IsStatsTy() && call.vargs.size() > 1) { call.addWarning() << "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. else if (call.vargs.at(0).type().IsMultiKeyMapTy()) { call.addError() << "Map type " << call.vargs.at(0).type() << " cannot print the value of individual keys. You must print " "the whole map."; } else if (call.vargs.at(0).type().IsPrintableTy()) { if (call.vargs.size() != 1) call.addError() << "Non-map print() only takes 1 argument, " << call.vargs.size() << " found"; } else { if (is_final_pass()) call.addError() << call.vargs.at(0).type() << " type passed to " << call.func << "() is not printable"; } } else if (call.func == "cgroup_path") { call.return_type = CreateCgroupPath(); } else if (call.func == "clear") { // Leave as `none`. } else if (call.func == "zero") { // Leave as `none`. } else if (call.func == "len") { if (!call.vargs.at(0).is() && !call.vargs.at(0).type().IsStack()) { call.addError() << "len() expects a map or stack to be provided"; } call.return_type = CreateInt64(); } else if (call.func == "time") { // Leave as `none`. } else if (call.func == "strftime") { call.return_type = CreateTimestamp(); if (is_final_pass()) { auto &arg = call.vargs.at(1); call.return_type.ts_mode = arg.type().ts_mode; if (call.return_type.ts_mode == TimestampMode::monotonic) { call.addError() << "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()) { call.addError() << "BPF_FUNC_send_signal not available for your kernel version"; } auto &arg = call.vargs.at(0); if (auto *sig = arg.as()) { if (signal_name_to_num(sig->value) < 1) { call.addError() << sig << " is not a valid signal"; } } else if (auto *integer = arg.as()) { if (integer->value < static_cast(1) || integer->value > static_cast(64)) { call.addError() << std::to_string(integer->value) << " is not a valid signal, allowed range: [1,64]"; } } else if (arg.is() || !arg.type().IsIntTy()) { call.addError() << "signal only accepts literal strings and positive integers"; } } else if (call.func == "path") { auto *probe = get_probe(call, call.func); if (probe == nullptr) return; if (!bpftrace_.feature_->has_d_path()) { call.addError() << "BPF_FUNC_d_path not available for your kernel version"; } // 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)) auto &arg = call.vargs.at(0); if (arg.type().GetTy() != Type::record && arg.type().GetTy() != Type::pointer) { call.addError() << "path() only supports pointer or record argument (" << arg.type().GetTy() << " provided)"; } auto call_type_size = bpftrace_.config_->max_strlen; if (call.vargs.size() == 2) { if (auto *size = call.vargs.at(1).as()) { call_type_size = size->value; } else { call.addError() << call.func << ": invalid size value, need non-negative literal"; } } call.return_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) call.addError() << "The path function can only be used with " << "'fentry', 'fexit', 'iter' probes"; } } else if (call.func == "strerror") { call.return_type = CreateStrerror(); } else if (call.func == "strncmp") { if (!call.vargs.at(2).is()) { call.addError() << "Builtin strncmp requires a non-negative literal"; } call.return_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 (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) { call.addWarning() << warning; } } call.return_type = CreateBool(); } else if (call.func == "override") { auto *probe = get_probe(call, call.func); if (probe == nullptr) return; for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::kprobe) { call.addError() << call.func << " can only be used with kprobes."; } } } else if (call.func == "kptr" || call.func == "uptr") { // kptr should accept both integer or pointer. Consider case: kptr($1) auto &arg = call.vargs.at(0); if (!arg.type().IsIntTy() && !arg.type().IsPtrTy()) { call.addError() << call.func << "() only supports " << "integer or pointer arguments (" << arg.type().GetTy() << " provided)"; return; } auto as = (call.func == "kptr" ? AddrSpace::kernel : AddrSpace::user); call.return_type = call.vargs.front().type(); call.return_type.SetAS(as); } else if (call.func == "macaddr") { auto &arg = call.vargs.at(0); if (!arg.type().IsIntTy() && !arg.type().IsArrayTy() && !arg.type().IsByteArray() && !arg.type().IsPtrTy()) call.addError() << call.func << "() only supports array or pointer arguments" << " (" << arg.type().GetTy() << " provided)"; if (arg.is()) call.addError() << call.func << "() does not support literal string arguments"; // N.B. When converting from BTF, we can treat string types as 7 bytes in // order to signal to userspace that they are well-formed. However, we can // convert from anything as long as there are at least 6 bytes to read. const auto &type = arg.type(); if ((type.IsArrayTy() || type.IsByteArray()) && type.GetSize() < 6) { call.addError() << call.func << "() argument must be at least 6 bytes in size"; } call.return_type = CreateMacAddress(); } else if (call.func == "unwatch") { // Leave as `none`. } else if (call.func == "bswap") { auto &arg = call.vargs.at(0); if (!arg.type().IsIntTy()) { call.addError() << call.func << "() only supports integer arguments (" << arg.type().GetTy() << " provided)"; return; } call.return_type = CreateUInt(arg.type().GetIntBitWidth()); } else if (call.func == "skboutput") { if (!bpftrace_.feature_->has_skb_output()) { call.addError() << "BPF_FUNC_skb_output is not available for your kernel " "version"; } call.return_type = CreateUInt32(); } else if (call.func == "nsecs") { call.return_type = CreateUInt64(); call.return_type.ts_mode = TimestampMode::boot; if (call.vargs.size() == 1) { call.return_type.ts_mode = call.vargs.at(0).type().ts_mode; } if (call.return_type.ts_mode == TimestampMode::tai && !bpftrace_.feature_->has_helper_ktime_get_tai_ns()) { call.addError() << "Kernel does not support tai timestamp, please try sw_tai"; } if (call.return_type.ts_mode == TimestampMode::sw_tai && !bpftrace_.delta_taitime_.has_value()) { call.addError() << "Failed to initialize sw_tai in " "userspace. This is very unexpected."; } } else if (call.func == "pid" || call.func == "tid") { call.return_type = CreateUInt32(); if (call.vargs.size() == 1) { auto &arg = call.vargs.at(0); if (!(arg.as())) { call.addError() << call.func << "() only supports curr_ns and init as the argument (" << arg.type().GetTy() << " provided)"; } } } else if (call.func == "socket_cookie") { auto logError = [&](T name) { call.addError() << call.func << "() only supports 'struct sock *' as the argument (" << name << " provided)"; }; const auto &type = call.vargs.at(0).type(); if (!type.IsPtrTy() || !type.GetPointeeTy() || !type.GetPointeeTy()->IsRecordTy()) { logError(type.GetTy()); return; } if (!type.GetPointeeTy()->IsSameType(CreateRecord("struct sock"))) { logError("'" + type.GetPointeeTy()->GetName() + " *'"); return; } call.return_type = CreateUInt64(); } else { // Check here if this corresponds to an external function. We convert the // external type metadata into the internal `SizedType` representation and // check that they are exactly equal. auto maybe_func = type_metadata_.global.lookup(call.func); if (!maybe_func) { call.addError() << "Unknown function: '" << call.func << "'"; return; } const auto &func = *maybe_func; if (func.linkage() != btf::Function::Linkage::Global && func.linkage() != btf::Function::Linkage::Extern) { call.addError() << "Unsupported function linkage: '" << call.func << "'"; return; } auto proto = func.type(); if (!proto) { call.addError() << "Unable to find function proto: " << proto.takeError(); return; } // Extract our return type. auto return_type = proto->return_type(); if (!return_type) { call.addError() << "Unable to read return type: " << return_type.takeError(); return; } auto compat_return_type = getCompatType(*return_type); if (!compat_return_type) { call.addError() << "Unable to convert return type: " << compat_return_type.takeError(); return; } call.return_type = *compat_return_type; // Convert all arguments. auto argument_types = proto->argument_types(); if (!argument_types) { call.addError() << "Unable to read argument types: " << argument_types.takeError(); return; } // Check the argument count. if (argument_types->size() != call.vargs.size()) { call.addError() << "Function `" << call.func << "` requires " << argument_types->size() << " arguments, got only " << call.vargs.size(); return; } std::vector> args; for (size_t i = 0; i < argument_types->size(); i++) { const auto &[name, type] = argument_types->at(i); auto compat_arg_type = getCompatType(type); if (!compat_arg_type) { // If the required type is a **pointer**, and the provided type is // a **pointer**, then we let it slide. Just assume the user knows // what they are doing. The verifier will catch them out otherwise. if (type.is() && call.vargs[i].type().IsPtrTy()) { args.emplace_back(name, call.vargs[i].type()); continue; } call.addError() << "Unable to convert argument type: " << compat_arg_type.takeError(); continue; } args.emplace_back(name, std::move(*compat_arg_type)); } if (args.size() != argument_types->size()) { return; // Already emitted errors. } // Check all the individual arguments. bool ok = true; for (size_t i = 0; i < args.size(); i++) { const auto &[name, type] = args[i]; if (type != call.vargs[i].type()) { if (!name.empty()) { call.vargs[i].node().addError() << "Expected " << typestr(type) << " for argument `" << name << "` got " << typestr(call.vargs[i].type()); } else { call.vargs[i].node().addError() << "Expected " << typestr(type) << " got " << typestr(call.vargs[i].type()); } ok = false; } } // Build our full proto as an error message. std::stringstream fullmsg; fullmsg << "Function `" << call.func << "` requires arguments ("; bool first = true; for (const auto &[name, type] : args) { if (!first) { fullmsg << ", "; } fullmsg << typestr(type); first = false; } fullmsg << ")"; if (!ok) { call.addError() << fullmsg.str(); return; } } } std::optional SemanticAnalyser::check(Sizeof &szof) { Visitor::visit(szof); if (std::holds_alternative(szof.record)) { auto &ty = std::get(szof.record); resolve_struct_type(ty, szof); if (!ty.IsNoneTy()) { return ty.GetSize(); } } else { const auto &ty = std::get(szof.record).type(); if (!ty.IsNoneTy()) { return ty.GetSize(); } } return std::nullopt; } void SemanticAnalyser::visit(Sizeof &szof) { const auto v = check(szof); if (!v && is_final_pass()) { szof.addError() << "sizeof not resolved, is type complete?"; } } std::optional SemanticAnalyser::check(Offsetof &offof) { Visitor::visit(offof); auto check_type = [&](SizedType record) -> std::optional { size_t offset = 0; // Check if all sub-fields are present. for (const auto &field : offof.field) { if (!record.IsRecordTy()) { offof.addError() << "'" << record << "' " << "is not a record type."; return std::nullopt; } else if (!bpftrace_.structs.Has(record.GetName())) { offof.addError() << "'" << record.GetName() << "' does not exist."; return std::nullopt; } else if (!record.HasField(field)) { offof.addError() << "'" << record.GetName() << "' " << "has no field named " << "'" << field << "'"; return std::nullopt; } else { // Get next sub-field const auto &f = record.GetField(field); offset += f.offset; record = f.type; } } return offset; }; std::optional offset; if (std::holds_alternative(offof.record)) { auto &ty = std::get(offof.record); resolve_struct_type(ty, offof); offset = check_type(ty); } else { const auto &ty = std::get(offof.record).type(); offset = check_type(ty); } if (offset) { return offset.value(); } return std::nullopt; } void SemanticAnalyser::visit(Offsetof &offof) { const auto v = check(offof); if (!v && is_final_pass()) { offof.addError() << "offsetof not resolved, is type complete?"; } } void SemanticAnalyser::check_stack_call(Call &call, bool kernel) { call.return_type = CreateStack(kernel); StackType stack_type; stack_type.mode = bpftrace_.config_->stack_mode; switch (call.vargs.size()) { case 0: break; case 1: { if (auto *ident = call.vargs.at(0).as()) { ConfigParser parser; auto ok = parser.parse(call.func, &stack_type.mode, ident->ident); if (!ok) { ident->addError() << "Error parsing stack mode: " << ok.takeError(); } } else if (check_arg(call, Type::integer, 0, true)) { if (auto *limit = call.vargs.at(0).as()) { stack_type.limit = limit->value; } else { call.addError() << call.func << ": invalid limit value"; } } break; } case 2: { if (auto *ident = call.vargs.at(0).as()) { ConfigParser parser; auto ok = parser.parse(call.func, &stack_type.mode, ident->ident); if (!ok) { ident->addError() << "Error parsing stack mode: " << ok.takeError(); } } else { // If two arguments are provided, then the first must be a stack mode. call.addError() << "Expected stack mode as first argument"; } if (check_arg(call, Type::integer, 1, true)) { if (auto *limit = call.vargs.at(1).as()) { stack_type.limit = limit->value; } else { call.addError() << call.func << ": invalid limit value"; } } break; } default: call.addError() << "Invalid number of arguments"; break; } constexpr int MAX_STACK_SIZE = 1024; if (stack_type.limit > MAX_STACK_SIZE) { call.addError() << call.func << "([int limit]): limit shouldn't exceed " << MAX_STACK_SIZE << ", " << stack_type.limit << " given"; } call.return_type = CreateStack(kernel, stack_type); } Probe *SemanticAnalyser::get_probe(Node &node, 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()) { node.addError() << "Feature not supported outside probe"; } else { node.addError() << "Builtin " << name << " not supported outside probe"; } } return probe; } void SemanticAnalyser::validate_map_key(const SizedType &key, Node &node) { if (key.IsPtrTy() && key.IsCtxAccess()) { // map functions only accepts a pointer to a element in the stack node.addError() << "context cannot be used as a map key"; } if (key.IsHistTy() || key.IsLhistTy() || key.IsStatsTy() || key.IsTSeriesTy()) { node.addError() << key << " cannot be used as a map key"; } if (is_final_pass() && key.IsNoneTy()) { node.addError() << "Invalid map key type: " << key; } } void SemanticAnalyser::visit(MapDeclStatement &decl) { const auto bpf_type = get_bpf_map_type(decl.bpf_type); if (!bpf_type) { auto &err = decl.addError(); err << "Invalid bpf map type: " << decl.bpf_type; auto &hint = err.addHint(); add_bpf_map_types_hint(hint); } else { bpf_map_type_.insert({ decl.ident, *bpf_type }); if (decl.max_entries != 1 && *bpf_type == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY) { decl.addError() << "Max entries can only be 1 for map type " << decl.bpf_type; } } if (is_final_pass()) { auto map_key_search_val = map_key_.find(decl.ident); if (map_key_search_val == map_key_.end()) { decl.addWarning() << "Unused map: " << decl.ident; } } } void SemanticAnalyser::visit(Map &map) { auto val = map_val_.find(map.ident); if (val != map_val_.end()) { map.value_type = val->second; } auto key = map_key_.find(map.ident); if (key != map_key_.end()) { map.key_type = key->second; } // Note that the naked `Map` node actually gets no type, the type // is applied to the node at the `MapAccess` level. if (is_final_pass()) { auto found_kind = bpf_map_type_.find(map.ident); if (found_kind != bpf_map_type_.end()) { if (!bpf_map_types_compatible(map.value_type, map_metadata_.scalar[map.ident], found_kind->second)) { auto map_type = get_bpf_map_type(map.value_type, map_metadata_.scalar[map.ident]); map.addError() << "Incompatible map types. Type from declaration: " << get_bpf_map_type_str(found_kind->second) << ". Type from value/key type: " << get_bpf_map_type_str(map_type); } } } } void SemanticAnalyser::visit(MapAddr &map_addr) { if (!map_val_.contains(map_addr.map->ident)) { if (!is_first_pass()) { map_addr.addError() << "Undefined map: " << map_addr.map->ident; } pass_tracker_.inc_num_unresolved(); } else { visit(map_addr.map); } } void SemanticAnalyser::check_variable(Variable &var, bool check_assigned) { if (auto *found = find_variable(var.ident)) { var.var_type = found->type; if (!found->was_assigned && check_assigned) { var.addWarning() << "Variable used before it was assigned: " << var.ident; } return; } var.addError() << "Undefined or undeclared variable: " << var.ident; } void SemanticAnalyser::visit(Variable &var) { check_variable(var, true); } void SemanticAnalyser::visit(VariableAddr &var_addr) { check_variable(*var_addr.var, false /* Don't warn if variable hasn't been assigned yet */); if (auto *found = find_variable(var_addr.var->ident)) { if (!found->type.IsNoneTy()) { var_addr.var_addr_type = CreatePointer(found->type, found->type.GetAS()); } } if (is_final_pass() && var_addr.var_addr_type.IsNoneTy()) { var_addr.addError() << "No type available for variable " << var_addr.var->ident; } } void SemanticAnalyser::visit(ArrayAccess &arr) { visit(arr.expr); visit(arr.indexpr); const SizedType &type = arr.expr.type(); if (is_final_pass()) { if (!type.IsArrayTy() && !type.IsPtrTy()) { arr.addError() << "The array index operator [] can only be " "used on arrays and pointers, found " << type.GetTy() << "."; return; } if (type.IsPtrTy() && type.GetPointeeTy()->GetSize() == 0) { arr.addError() << "The array index operator [] cannot be used " "on a pointer to an unsized type (void *)."; } if (auto *integer = arr.indexpr.as()) { if (type.IsArrayTy()) { size_t num = type.GetNumElements(); if (num != 0 && static_cast(integer->value) >= num) { arr.addError() << "the index " << integer->value << " is out of bounds for array of size " << num; } } } else if (!arr.indexpr.type().IsIntTy() || arr.indexpr.type().IsSigned()) { arr.addError() << "The array index operator [] only " "accepts positive (unsigned) integer indices. Got: " << arr.indexpr.type(); } } if (type.IsArrayTy()) arr.element_type = *type.GetElementTy(); else if (type.IsPtrTy()) arr.element_type = *type.GetPointeeTy(); arr.element_type.SetAS(type.GetAS()); // BPF verifier cannot track BTF information for double pointers so we // cannot propagate is_internal 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.element_type.IsPtrTy() || type.IsPtrTy()) { arr.element_type.is_internal = false; } else { arr.element_type.is_internal = type.is_internal; } } void SemanticAnalyser::visit(TupleAccess &acc) { visit(acc.expr); const SizedType &type = acc.expr.type(); if (!type.IsTupleTy()) { if (is_final_pass()) { acc.addError() << "Can not access index '" << acc.index << "' on expression of type '" << type << "'"; } return; } bool valid_idx = 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()) { acc.addError() << "Invalid tuple index: " << acc.index << ". Found " << type.GetFields().size() << " elements in tuple."; } if (valid_idx) { acc.element_type = type.GetField(acc.index).type; } } 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; std::optional left_literal; std::optional right_literal; if (auto *integer = left.as()) left_literal.emplace(static_cast(integer->value)); if (auto *integer = left.as()) left_literal.emplace(integer->value); if (auto *integer = right.as()) right_literal.emplace(static_cast(integer->value)); if (auto *integer = right.as()) right_literal.emplace(integer->value); // 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.value() >= 0) { lsign = false; } // The reverse (10 < a) should also hold else if (!lsign && rsign && right_literal && right_literal.value() >= 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: binop.addWarning() << "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: binop.addWarning() << "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.value() >= 0) lsign = false; if (rsign && right_literal && right_literal.value() >= 0) rsign = false; // If they're still signed, we have to warn if (lsign || rsign) { binop.addWarning() << "signed operands for '" << opstr(binop) << "' can lead to undefined behavior " << "(cast to unsigned to silence warning)"; } } } void SemanticAnalyser::binop_array(Binop &binop) { const auto &lht = binop.left.type(); const auto &rht = binop.right.type(); if (binop.op != Operator::EQ && binop.op != Operator::NE) { binop.addError() << "The " << opstr(binop) << " operator cannot be used on arrays."; } if (lht.GetNumElements() != rht.GetNumElements()) { binop.addError() << "Only arrays of same size support comparison operators."; } if (!lht.GetElementTy()->IsIntegerTy() || lht != rht) { binop.addError() << "Only arrays of same sized integer support comparison operators."; } } void SemanticAnalyser::binop_ptr(Binop &binop) { const auto &lht = binop.left.type(); const auto &rht = binop.right.type(); bool left_is_ptr = lht.IsPtrTy(); const auto &ptr = left_is_ptr ? lht : rht; const 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, &lht, &rht]() { binop.addError() << "The " << opstr(binop) << " operator can not be used on expressions of types " << lht << ", " << rht; }; // Binop on two pointers if (other.IsPtrTy()) { if (compare) { if (is_final_pass()) { const auto *le = lht.GetPointeeTy(); const auto *re = rht.GetPointeeTy(); if (*le != *re) { auto &warn = binop.addWarning(); warn << "comparison of distinct pointer types: " << *le << ", " << *re; warn.addContext(binop.left.loc()) << "left (" << *le << ")"; warn.addContext(binop.right.loc()) << "right (" << *re << ")"; } } } else if (!logical) { invalid_op(); } } // Binop on a pointer and (int or bool) else if (other.IsIntTy() || other.IsBoolTy()) { // 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.result_type = CreatePointer(*ptr.GetPointeeTy(), ptr.GetAS()); else if (!compare && !logical) 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) { visit(binop.left); visit(binop.right); const auto &lht = binop.left.type(); const 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() || lht.IsBoolTy()) && (rht.IsCastableMapTy() || rht.IsIntTy() || rht.IsBoolTy()); bool is_signed = lsign && rsign; bool is_comparison = is_comparison_op(binop.op); switch (binop.op) { case Operator::LEFT: case Operator::RIGHT: is_signed = lsign; break; default: break; } if (is_comparison) { binop.result_type = CreateBool(); } if (lht.IsBoolTy() && rht.IsBoolTy()) { binop.result_type = CreateBool(); return; } if (lht.IsPtrTy() || rht.IsPtrTy()) { binop_ptr(binop); return; } if (!is_comparison) { if (is_int_binop) { // Implicit size promotion to larger of the two auto size = std::max(lht.GetSize(), rht.GetSize()); binop.result_type = CreateInteger(size * 8, is_signed); } else { // Default type - will be overriden below as necessary binop.result_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()) binop.addWarning() << "Addrspace mismatch"; binop.result_type.SetAS(AddrSpace::none); } // Associativity from left to right for binary operator else if (addr_lhs != AddrSpace::none) { binop.result_type.SetAS(addr_lhs); } else { // In case rhs is none, then this triggers warning in // selectProbeReadHelper. binop.result_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()) { auto &err = binop.addError(); err << "Type mismatch for '" << opstr(binop) << "': comparing " << lht << " with " << rht; err.addContext(binop.left.loc()) << "left (" << lht << ")"; err.addContext(binop.right.loc()) << "right (" << rht << ")"; } // Also allow combination like reg("sp") + 8 else if (binop.op != Operator::EQ && binop.op != Operator::NE) { binop.addError() << "The " << opstr(binop) << " operator can not be used on expressions of types " << lht << ", " << rht; } } 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 (auto *acc = unop.expr.as()) { auto *maptype = get_map_type(*acc->map); if (!maptype) { // Doing increments or decrements on the map type implements that // it is done on an integer. Maps are always coerced into larger // integers, so this should not conflict with different assignments. assign_map_type(*acc->map, CreateInt64(), acc->map); } } else if (!unop.expr.is()) { unop.addError() << "The " << opstr(unop) << " operator must be applied to a map or variable"; } } visit(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:; } const SizedType &type = unop.expr.type(); if (is_final_pass()) { bool invalid = false; // Unops are only allowed on ints (e.g. ~$x), dereference only on pointers // and context (we allow args->field for backwards compatibility) if (type.IsBoolTy()) { invalid = unop.op != Operator::LNOT; } else if (!type.IsIntegerTy() && !((type.IsPtrTy() || type.IsCtxAccess()) && valid_ptr_op)) { invalid = true; } if (invalid) { unop.addError() << "The " << opstr(unop) << " operator can not be used on expressions of type '" << type << "'"; } } if (unop.op == Operator::MUL) { if (type.IsPtrTy()) { unop.result_type = SizedType(*type.GetPointeeTy()); if (type.IsCtxAccess()) unop.result_type.MarkCtxAccess(); unop.result_type.is_internal = type.is_internal; unop.result_type.SetAS(type.GetAS()); } else if (type.IsRecordTy()) { // We allow dereferencing "args" with no effect (for backwards compat) if (type.IsCtxAccess()) unop.result_type = type; else { unop.addError() << "Can not dereference struct/union of type '" << type.GetName() << "'. It is not a pointer."; } } else if (type.IsIntTy()) { unop.result_type = CreateUInt64(); } } else if (unop.op == Operator::LNOT) { unop.result_type = CreateBool(); } else if (type.IsPtrTy() && valid_ptr_op) { unop.result_type = unop.expr.type(); } else { unop.result_type = CreateInteger(64, type.IsSigned()); } } void SemanticAnalyser::visit(Ternary &ternary) { visit(ternary.cond); visit(ternary.left); visit(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()) { ternary.addError() << "Ternary operator must return the same type: " << "have '" << lhs << "' and '" << rhs << "'"; } // This assignment is just temporary to prevent errors // before the final pass ternary.result_type = lhs; return; } if (lhs.IsStack() && lhs.stack_type != rhs.stack_type) { // TODO: fix this for different stack types ternary.addError() << "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 && cond != Type::boolean) { ternary.addError() << "Invalid condition in ternary: " << cond; return; } if (lhs.IsIntegerTy()) { ternary.result_type = CreateInteger(64, lhs.IsSigned()); } else { auto lsize = lhs.GetSize(); auto rsize = rhs.GetSize(); if (lhs.IsTupleTy()) { ternary.result_type = create_merged_tuple(rhs, lhs); } else { ternary.result_type = lsize > rsize ? lhs : rhs; } } } void SemanticAnalyser::visit(If &if_node) { visit(if_node.cond); if (is_final_pass()) { const Type &cond = if_node.cond.type().GetTy(); if (cond != Type::integer && cond != Type::pointer && cond != Type::boolean) if_node.addError() << "Invalid condition in if(): " << cond; } visit(if_node.if_block); visit(if_node.else_block); } void SemanticAnalyser::visit(Unroll &unroll) { visit(unroll.expr); auto *integer = unroll.expr.as(); if (!integer) { unroll.addError() << "invalid unroll value"; return; } if (integer->value > static_cast(100)) { unroll.addError() << "unroll maximum value is 100"; } else if (integer->value < static_cast(1)) { unroll.addError() << "unroll minimum value is 1"; } visit(unroll.block); } void SemanticAnalyser::visit(Jump &jump) { switch (jump.ident) { case JumpType::RETURN: if (jump.return_value) { visit(jump.return_value); } if (auto *subprog = dynamic_cast(top_level_node_)) { if ((subprog->return_type.IsVoidTy() != !jump.return_value.has_value()) || (jump.return_value.has_value() && jump.return_value->type() != subprog->return_type)) { jump.addError() << "Function " << subprog->name << " is of type " << subprog->return_type << ", cannot return " << (jump.return_value.has_value() ? jump.return_value->type() : CreateVoid()); } } break; case JumpType::BREAK: case JumpType::CONTINUE: if (!in_loop()) jump.addError() << opstr(jump) << " used outside of a loop"; break; default: jump.addError() << "Unknown jump: '" << opstr(jump) << "'"; } } void SemanticAnalyser::visit(While &while_block) { visit(while_block.cond); loop_depth_++; visit(while_block.block); loop_depth_--; } void SemanticAnalyser::visit(For &f) { if (f.iterable.is() && !bpftrace_.feature_->has_helper_loop()) { f.addError() << "Missing required kernel feature: loop"; } if (f.iterable.is() && !bpftrace_.feature_->has_helper_for_each_map_elem()) { f.addError() << "Missing required kernel feature: for_each_map_elem"; } if (auto *map = f.iterable.as()) { if (!is_first_pass() && !map_val_.contains(map->ident)) { map->addError() << "Undefined map: " << map->ident; } } // For-loops are implemented using the bpf_for_each_map_elem or bpf_loop // helper functions, 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)) { f.decl->addError() << "Loop declaration shadows existing variable: " + decl_name; } visit(f.iterable); // Validate the iterable. if (auto *map = f.iterable.as()) { if (!map->type().IsMapIterableTy()) { map->addError() << "Loop expression does not support type: " << map->type(); return; } } else if (auto *range = f.iterable.as()) { if (is_final_pass()) { if (!range->start.type().IsIntTy()) { range->addError() << "Loop range requires an integer for the start value"; } if (!range->end.type().IsIntTy()) { range->addError() << "Loop range requires an integer for the end value"; } } } // Validate body. We may relax this in the future. CollectNodes jumps; jumps.visit(f.stmts); for (const Jump &n : jumps.nodes()) { if (n.ident == JumpType::RETURN) { n.addError() << "'" << opstr(n) << "' statement is not allowed in a for-loop"; } } if (!ctx_.diagnostics().ok()) 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); auto &collector = iter->second; collector.visit(stmt, [this, &found_vars](const auto &var) { if (found_vars.contains(var.ident)) return false; if (find_variable(var.ident)) { found_vars.insert(var.ident); return true; } return false; }); } } // Create type for the loop's decl. if (auto *map = f.iterable.as()) { // 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 (!mapkey || !mapval) return; f.decl->var_type = CreateTuple(Struct::CreateTuple({ *mapkey, *mapval })); } else if (auto *range = f.iterable.as()) { // Always use the same type as the first parameter. f.decl->var_type = range->start.type(); } 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; builtins.visit(f.stmts); for (const Builtin &builtin : builtins.nodes()) { if (builtin.builtin_type.IsCtxAccess() || builtin.is_argx() || builtin.ident == "__builtin_retval") { builtin.addError() << "'" << 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); auto &collector = iter->second; for (const Variable &var : collector.nodes()) { ctx_types.push_back(CreatePointer(var.var_type, AddrSpace::kernel)); ctx_idents.push_back(var.ident); } f.ctx_type = CreateRecord(Struct::CreateRecord(ctx_types, ctx_idents)); } void SemanticAnalyser::visit(FieldAccess &acc) { visit(acc.expr); const SizedType &type = acc.expr.type(); if (type.IsPtrTy()) { acc.addError() << "Can not access field '" << acc.field << "' on type '" << type << "'. Try dereferencing it first, or using '->'"; return; } if (!type.IsRecordTy()) { if (is_final_pass()) { acc.addError() << "Can not access field '" << acc.field << "' on expression of type '" << type << "'"; } return; } if (type.is_funcarg) { auto *probe = get_probe(acc); if (probe == nullptr) return; const auto *arg = bpftrace_.structs.GetProbeArg(*probe, acc.field); if (arg) { acc.field_type = arg->type; acc.field_type.SetAS(acc.expr.type().GetAS()); if (is_final_pass() && acc.field_type.IsNoneTy()) { acc.addError() << acc.field << " has unsupported type"; } } else { acc.addError() << "Can't find function parameter " << acc.field; } return; } if (!bpftrace_.structs.Has(type.GetName())) { acc.addError() << "Unknown struct/union: '" << type.GetName() << "'"; return; } std::map> structs; if (type.is_tparg) { auto *probe = get_probe(acc); 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; } std::string tracepoint_struct = TracepointFormatParser::get_struct_name( *attach_point); structs[tracepoint_struct] = bpftrace_.structs.Lookup(tracepoint_struct).lock(); } } else { structs[type.GetName()] = type.GetStruct(); } for (auto it : structs) { std::string cast_type = it.first; const auto record = it.second; if (!record->HasField(acc.field)) { acc.addError() << "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) { acc.addError() << "Attempting to access pointer field '" << acc.field << "' with unsupported tag attribute: " << tag; } } } acc.field_type = field.type; if (acc.expr.type().IsCtxAccess() && (acc.field_type.IsArrayTy() || acc.field_type.IsRecordTy())) { // e.g., ((struct bpf_perf_event_data*)ctx)->regs.ax acc.field_type.MarkCtxAccess(); } acc.field_type.is_internal = type.is_internal; acc.field_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) acc.addError() << "BPF does not support accessing common tracepoint fields"; } } } void SemanticAnalyser::visit(MapAccess &acc) { visit(acc.map); visit(acc.key); reconcile_map_key(acc.map, acc.key); auto search_val = map_val_.find(acc.map->ident); if (search_val != map_val_.end()) { if (acc.map->type().IsCastableMapTy() && !bpftrace_.feature_->has_helper_map_lookup_percpu_elem()) { acc.addError() << "Missing required kernel feature: map_lookup_percpu_elem"; } acc.map->value_type = search_val->second; } else { // If there is no record of any assignment after the first pass // then it's safe to say this map is undefined. bool read_only = named_param_defaults_.defaults.contains(acc.map->ident); if (!is_first_pass() && !read_only) { acc.addError() << "Undefined map: " << acc.map->ident; } pass_tracker_.inc_num_unresolved(); } } void SemanticAnalyser::reconcile_map_key(Map *map, const Expression &key_expr) { SizedType new_key_type = create_key_type(key_expr.type(), key_expr.node()); if (const auto &key = map_key_.find(map->ident); key != map_key_.end()) { update_current_key(key->second, new_key_type); validate_new_key(key->second, new_key_type, map->ident, key_expr); } else { if (!new_key_type.IsNoneTy()) { map_key_.insert({ map->ident, new_key_type }); map->key_type = new_key_type; } } } // 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) { visit(cast.expr); // cast type is synthesised in parser, if it is a struct, it needs resolving resolve_struct_type(cast.cast_type, cast); auto rhs = cast.expr.type(); if (rhs.IsRecordTy()) { cast.addError() << "Cannot cast from struct type \"" << cast.expr.type() << "\""; } else if (rhs.IsNoneTy()) { cast.addError() << "Cannot cast from \"" << cast.expr.type() << "\" type"; } if (!cast.cast_type.IsIntTy() && !cast.cast_type.IsPtrTy() && !cast.cast_type.IsBoolTy() && (!cast.cast_type.IsPtrTy() || cast.cast_type.GetElementTy()->IsIntTy() || cast.cast_type.GetElementTy()->IsRecordTy()) && // we support casting integers to int arrays !(cast.cast_type.IsArrayTy() && cast.cast_type.GetElementTy()->IsBoolTy()) && !(cast.cast_type.IsArrayTy() && cast.cast_type.GetElementTy()->IsIntTy())) { auto &err = cast.addError(); err << "Cannot cast to \"" << cast.cast_type << "\""; if (auto it = KNOWN_TYPE_ALIASES.find(cast.cast_type.GetName()); it != KNOWN_TYPE_ALIASES.end()) { err.addHint() << "Did you mean \"" << it->second << "\"?"; } } if (cast.cast_type.IsArrayTy()) { if (cast.cast_type.GetNumElements() == 0) { if (cast.cast_type.GetElementTy()->GetSize() == 0) cast.addError() << "Could not determine size of the array"; else { if (rhs.GetSize() % cast.cast_type.GetElementTy()->GetSize() != 0) { cast.addError() << "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.cast_type.GetElementTy()->GetSize(); cast.cast_type = CreateArray(num_elems, *cast.cast_type.GetElementTy()); } } if (rhs.IsIntTy() || rhs.IsBoolTy()) cast.cast_type.is_internal = true; } if (cast.cast_type.IsEnumTy()) { if (!c_definitions_.enum_defs.contains(cast.cast_type.GetName())) { cast.addError() << "Unknown enum: " << cast.cast_type.GetName(); } else { if (auto *integer = cast.expr.as()) { if (!c_definitions_.enum_defs[cast.cast_type.GetName()].contains( integer->value)) { cast.addError() << "Enum: " << cast.cast_type.GetName() << " doesn't contain a variant value of " << integer->value; } } } } if (cast.cast_type.IsBoolTy() && !rhs.IsIntTy() && !rhs.IsStringTy() && !rhs.IsPtrTy() && !rhs.IsCastableMapTy()) { if (is_final_pass()) { cast.addError() << "Cannot cast from \"" << rhs << "\" to \"" << cast.cast_type << "\""; } } if ((cast.cast_type.IsIntTy() && !rhs.IsIntTy() && !rhs.IsPtrTy() && !rhs.IsBoolTy() && !rhs.IsCtxAccess() && !rhs.IsArrayTy() && !rhs.IsCastableMapTy()) || // casting from/to int arrays must respect the size (cast.cast_type.IsArrayTy() && (!rhs.IsBoolTy() || cast.cast_type.GetSize() != rhs.GetSize()) && (!rhs.IsIntTy() || cast.cast_type.GetSize() != rhs.GetSize())) || (rhs.IsArrayTy() && (!cast.cast_type.IsIntTy() || cast.cast_type.GetSize() != rhs.GetSize()))) { cast.addError() << "Cannot cast from \"" << rhs << "\" to \"" << cast.cast_type << "\""; } if (cast.expr.type().IsCtxAccess() && !cast.cast_type.IsIntTy()) cast.cast_type.MarkCtxAccess(); cast.cast_type.SetAS(cast.expr.type().GetAS()); // case : begin { @foo = (struct Foo)0; } // case : profile:hz:99 $task = (struct task_struct *)curtask. if (cast.cast_type.GetAS() == AddrSpace::none) { if (auto *probe = dynamic_cast(top_level_node_)) { ProbeType type = single_provider_type(probe); cast.cast_type.SetAS(find_addrspace(type)); } else { // Assume kernel space for data in subprogs cast.cast_type.SetAS(AddrSpace::kernel); } } } void SemanticAnalyser::visit(Tuple &tuple) { std::vector elements; for (auto &elem : tuple.elems) { visit(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().IsMultiKeyMapTy()) { elem.node().addError() << "Map type " << elem.type() << " cannot exist inside a tuple."; } elements.emplace_back(elem.type()); } tuple.tuple_type = CreateTuple(Struct::CreateTuple(elements)); } void SemanticAnalyser::visit(Expression &expr) { // Visit and fold all other values. Visitor::visit(expr); fold(ctx_, expr); // Inline specific constant expressions. if (auto *szof = expr.as()) { const auto v = check(*szof); if (v) { expr.value = ctx_.make_node(*v, Location(szof->loc), /*force_unsigned=*/true); } } else if (auto *offof = expr.as()) { const auto v = check(*offof); if (v) { expr.value = ctx_.make_node(*v, Location(offof->loc), /*force_unsigned=*/true); } } } void SemanticAnalyser::visit(ExprStatement &expr) { if (auto *call = expr.expr.as()) { // Calls from expression statements are bare, meaning they're not // handling the return value e.g. // delete(@a, 1); <- ExprStatement // vs // $x = delete(@a, 1) <- AssignVarStatement // if (delete(@a, 1)) { <- If call->ret_val_discarded = true; } visit(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::tseries_t, "tseries(rand %10, 10s, 1)" }, { Type::stats_t, "stats(arg2)" }, }; void SemanticAnalyser::visit(AssignMapStatement &assignment) { visit(assignment.map); visit(assignment.key); visit(assignment.expr); reconcile_map_key(assignment.map, assignment.key); const auto *map_type_before = get_map_type(*assignment.map); // 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(); if (map_contains_int && assignment.expr.type().IsCastableMapTy()) { assignment.expr = ctx_.make_node(*map_type_before, assignment.expr, Location(assignment.loc)); } if (!is_valid_assignment(assignment.expr, map_type_before == nullptr)) { auto &err = assignment.addError(); const auto &type = assignment.expr.type(); auto hint = AGGREGATE_HINTS.find(type.GetTy()); if (hint == AGGREGATE_HINTS.end()) { err << "Not a valid assignment: " << type.GetTy(); } else { 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 (const auto *acc = assignment.expr.as()) { if (type.IsCastableMapTy()) { err.addHint() << "Add a cast to integer if you want the value of the " "aggregate, " << "e.g. `" << assignment.map->ident << " = (int64)" << acc->map->ident << ";`."; } } } } assign_map_type( *assignment.map, assignment.expr.type(), &assignment, &assignment); 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) { assignment.addError() << "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) { assignment.addWarning() << "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."; assignment.addWarning() << buf.str(); } else { // bpf_map_update_elem() expects map_size-length value assignment.addError() << buf.str(); } } } else if (type.IsCtxAccess()) { // bpf_map_update_elem() only accepts a pointer to a element in the stack assignment.addError() << "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)) { assignment.addError() << "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 { assignment.addError() << "Array type mismatch: " << map_type << " != " << expr_type << "."; } } else if (type.IsNoneTy()) { pass_tracker_.inc_num_unresolved(); } } void SemanticAnalyser::visit(AssignVarStatement &assignment) { visit(assignment.expr); // Only visit the declaration if it is a `let` declaration, // otherwise skip as it is not a variable access. if (std::holds_alternative(assignment.var_decl)) { visit(assignment.var_decl); } if (assignment.expr.type().IsCastableMapTy()) { assignment.expr = ctx_.make_node(CreateInt64(), assignment.expr, Location(assignment.loc)); } if (!is_valid_assignment(assignment.expr, false)) { if (is_final_pass()) { assignment.addError() << "Value '" << assignment.expr.type() << "' cannot be assigned to a scratch variable."; } return; } Node *var_scope = nullptr; const auto &var_ident = assignment.var()->ident; 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) && (!storedTy.IsIntegerTy() || !assignTy.IsIntegerTy())) { 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 (storedTy.IsEqual(assignTy)) { // No checks or casts needed. } else if (auto *neg_integer = assignment.expr.as()) { int64_t value = neg_integer->value; if (!storedTy.IsSigned()) { type_mismatch_error = true; } else { auto min_max = getIntTypeRange(storedTy); if (value < min_max.first) { assignment.addError() << "Type mismatch for " << var_ident << ": " << "trying to assign value '" << neg_integer->value << "' which does not fit into the variable of type '" << storedTy << "'"; } else { assignTy = storedTy; assignment.expr = ctx_.make_node( CreateInteger(storedTy.GetSize() * 8, true), assignment.expr, Location(assignment.loc)); visit(assignment.expr); } } } else if (auto *integer = assignment.expr.as()) { uint64_t value = integer->value; bool can_fit = false; if (!storedTy.IsSigned()) { auto min_max = getUIntTypeRange(storedTy); can_fit = value <= min_max.second; } else { auto min_max = getIntTypeRange(storedTy); can_fit = value <= static_cast(min_max.second); } if (can_fit) { assignTy = storedTy; assignment.expr = ctx_.make_node( CreateInteger(storedTy.GetSize() * 8, storedTy.IsSigned()), assignment.expr, Location(assignment.loc)); visit(assignment.expr); } else { assignment.addError() << "Type mismatch for " << var_ident << ": " << "trying to assign value '" << static_cast(integer->value) << "' 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)) { assignment.addError() << "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) { assignment.addWarning() << "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) { const auto *err_segment = foundVar.was_assigned ? "when variable already contains a value of type" : "when variable already has a type"; assignment.addError() << "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()->var_type = storedTy; if (is_final_pass()) { if (storedTy.IsNoneTy()) assignment.addError() << "Invalid expression for assignment: " << storedTy; } } void SemanticAnalyser::visit(VarDeclStatement &decl) { const std::string &var_ident = decl.var->ident; if (decl.type && !IsValidVarDeclType(*decl.type)) { decl.addError() << "Invalid variable declaration type: " << *decl.type; } else if (decl.type && decl.var->var_type.IsNoneTy()) { decl.var->var_type = *decl.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) { decl.addError() << "Variable " << var_ident << " was already declared. Variable shadowing is not allowed."; decl_search->second.addWarning() << "This is the 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()) { decl.addError() << "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->var_type = foundVar.type; } if (is_final_pass() && !foundVar.was_assigned) { decl.addWarning() << "Variable " << var_ident << " never assigned to."; } return; } } bool can_resize = decl.var->var_type.GetSize() == 0; variables_[scope_stack_.back()].insert({ var_ident, { .type = decl.var->var_type, .can_resize = can_resize, .was_assigned = false } }); variable_decls_[scope_stack_.back()].insert({ var_ident, decl }); } void SemanticAnalyser::visit(Predicate &pred) { visit(pred.expr); if (is_final_pass()) { const auto &ty = pred.expr.type(); if (!ty.IsIntTy() && !ty.IsPtrTy() && !ty.IsBoolTy()) { pred.addError() << "Invalid type for predicate: " << pred.expr.type().GetTy(); } } } void SemanticAnalyser::visit(AttachPoint &ap) { if (ap.provider == "kprobe" || ap.provider == "kretprobe") { if (ap.func.empty()) ap.addError() << "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_->missing_probes != ConfigMissingProbes::ignore && !util::has_wildcard(ap.func) && !bpftrace_.is_traceable_func(ap.func)) { ap.addWarning() << 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.empty()) ap.addError() << ap.provider << " should have a target"; if (ap.func.empty() && ap.address == 0) ap.addError() << ap.provider << " should be attached to a function and/or address"; if (!ap.lang.empty() && !is_supported_lang(ap.lang)) ap.addError() << "unsupported language type: " << ap.lang; if (ap.provider == "uretprobe" && ap.func_offset != 0) ap.addError() << "uretprobes can not be attached to a function offset"; auto get_paths = [&]() -> Result> { const auto pid = bpftrace_.pid(); if (ap.target == "*") { if (pid.has_value()) return util::get_mapped_paths_for_pid(*pid); else return util::get_mapped_paths_for_running_pids(); } else { return util::resolve_binary_path(ap.target, pid); } }; auto paths = get_paths(); if (!paths) { // There was an error during path resolution. ap.addError() << "error finding uprobe target: " << paths.takeError(); } else { switch (paths->size()) { case 0: ap.addError() << "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) { ap.addWarning() << "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.empty()) ap.addError() << "usdt probe must have a target function or wildcard"; if (!ap.target.empty() && !(bpftrace_.pid().has_value() && util::has_wildcard(ap.target))) { auto paths = util::resolve_binary_path(ap.target, bpftrace_.pid()); switch (paths.size()) { case 0: ap.addError() << "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) { ap.addWarning() << "attaching to usdt target file '" << paths.front() << "' but matched " << std::to_string(paths.size()) << " binaries"; ap.target = paths.front(); } } } const auto pid = bpftrace_.pid(); if (pid.has_value()) { USDTHelper::probes_for_pid(*pid); } else if (ap.target == "*") { USDTHelper::probes_for_all_pids(); } else if (!ap.target.empty()) { for (auto &path : util::resolve_binary_path(ap.target)) USDTHelper::probes_for_path(path); } else { ap.addError() << "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.empty() || ap.func.empty()) ap.addError() << "tracepoint probe must have a target"; } else if (ap.provider == "rawtracepoint") { if (ap.func.empty()) ap.addError() << "rawtracepoint should be attached to a function"; if (!listing_ && !bpftrace_.has_btf_data()) { ap.addError() << "rawtracepoints require kernel BTF. Try using a " "'tracepoint' instead."; } } else if (ap.provider == "profile") { if (ap.target.empty()) ap.addError() << "profile probe must have unit of time"; else if (!listing_) { if (!TIME_UNITS.contains(ap.target)) ap.addError() << ap.target << " is not an accepted unit of time"; if (!ap.func.empty()) ap.addError() << "profile probe must have an integer frequency"; else if (ap.freq <= 0) ap.addError() << "profile frequency should be a positive integer"; } } else if (ap.provider == "interval") { if (ap.target.empty()) ap.addError() << "interval probe must have unit of time"; else if (!listing_) { if (!TIME_UNITS.contains(ap.target)) ap.addError() << ap.target << " is not an accepted unit of time"; if (!ap.func.empty()) ap.addError() << "interval probe must have an integer frequency"; else if (ap.freq <= 0) ap.addError() << "interval frequency should be a positive integer"; } } else if (ap.provider == "software") { if (ap.target.empty()) ap.addError() << "software probe must have a software event name"; else { if (!util::has_wildcard(ap.target) && !ap.ignore_invalid) { bool found = false; for (const auto &probeListItem : SW_PROBE_LIST) { if (ap.target == probeListItem.path || (!probeListItem.alias.empty() && ap.target == probeListItem.alias)) { found = true; break; } } if (!found) ap.addError() << ap.target << " is not a software probe"; } else if (!listing_) { ap.addError() << "wildcards are not allowed for hardware probe type"; } } if (!ap.func.empty()) ap.addError() << "software probe can only have an integer count"; else if (ap.freq < 0) ap.addError() << "software count should be a positive integer"; } else if (ap.provider == "watchpoint" || ap.provider == "asyncwatchpoint") { if (!ap.func.empty()) { if (!bpftrace_.pid().has_value() && !has_child_) ap.addError() << "-p PID or -c CMD required for watchpoint"; if (ap.address >= static_cast(arch::Host::arguments().size())) ap.addError() << arch::Host::Machine << " doesn't support arg" << ap.address; } else if (ap.provider == "asyncwatchpoint") ap.addError() << ap.provider << " requires a function name"; else if (!ap.address) ap.addError() << "watchpoint must be attached to a non-zero address"; if (ap.len != 1 && ap.len != 2 && ap.len != 4 && ap.len != 8) ap.addError() << "watchpoint length must be one of (1,2,4,8)"; if (ap.mode.empty()) ap.addError() << "watchpoint mode must be combination of (r,w,x)"; std::ranges::sort(ap.mode); for (const char c : ap.mode) { if (c != 'r' && c != 'w' && c != 'x') ap.addError() << "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]) ap.addError() << "watchpoint modes may not be duplicated"; } const auto &modes = arch::Host::watchpoint_modes(); if (!modes.contains(ap.mode)) { if (modes.empty()) { // There are no valid modes. ap.addError() << "watchpoints not supported"; } else { // Build a suitable error with hint. auto &err = ap.addError(); err << "invalid watchpoint mode: " << ap.mode; err.addHint() << "supported modes: " << util::str_join(std::vector(modes.begin(), modes.end()), ","); } } } else if (ap.provider == "hardware") { if (ap.target.empty()) ap.addError() << "hardware probe must have a hardware event name"; else { if (!util::has_wildcard(ap.target) && !ap.ignore_invalid) { bool found = false; for (const auto &probeListItem : HW_PROBE_LIST) { if (ap.target == probeListItem.path || (!probeListItem.alias.empty() && ap.target == probeListItem.alias)) { found = true; break; } } if (!found) ap.addError() << ap.target + " is not a hardware probe"; } else if (!listing_) { ap.addError() << "wildcards are not allowed for hardware probe type"; } } if (!ap.func.empty()) ap.addError() << "hardware probe can only have an integer count"; else if (ap.freq < 0) ap.addError() << "hardware frequency should be a positive integer"; } else if (ap.provider == "begin" || ap.provider == "end") { if (!ap.target.empty() || !ap.func.empty()) ap.addError() << "begin/end probes should not have a target"; if (is_final_pass()) { if (ap.provider == "begin") { if (has_begin_probe_) ap.addError() << "More than one begin probe defined"; has_begin_probe_ = true; } if (ap.provider == "end") { if (has_end_probe_) ap.addError() << "More than one end probe defined"; has_end_probe_ = true; } } } else if (ap.provider == "self") { if (ap.target == "signal") { if (!SIGNALS.contains(ap.func)) ap.addError() << ap.func << " is not a supported signal"; return; } ap.addError() << ap.target << " is not a supported trigger"; } else if (ap.provider == "bench") { if (ap.target.empty()) ap.addError() << "bench probes must have a name"; if (is_final_pass()) { auto it = benchmark_locs_.find(ap.target); if (it != benchmark_locs_.end()) { auto &err = ap.addError(); err << "\"" + ap.target + "\"" << " was used as the name for more than one BENCH probe"; err.addContext(it->second) << "this is the other instance"; } benchmark_locs_.emplace(ap.target, ap.loc); } } else if (ap.provider == "fentry" || ap.provider == "fexit") { if (!bpftrace_.feature_->has_fentry()) { ap.addError() << "fentry/fexit not available for your kernel version."; return; } if (ap.func.empty()) ap.addError() << "fentry/fexit should specify a function"; } else if (ap.provider == "iter") { if (!listing_ && !bpftrace_.btf_->get_all_iters().contains(ap.func)) { ap.addError() << "iter " << ap.func << " not available for your kernel version."; } if (ap.func.empty()) ap.addError() << "iter should specify a iterator's name"; } else { ap.addError() << "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(BlockExpr &block_expr) { scope_stack_.push_back(&block_expr); accept_statements(block_expr.stmts); visit(block_expr.expr); 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") { if (util::has_wildcard(ap->raw_input)) ap->addError() << "iter probe type does not support wildcards"; else ap->addError() << "Only single iter attach point is allowed."; return; } visit(ap); } visit(probe.pred); visit(probe.block); } 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 (ctx_.diagnostics().ok()) { pass_tracker_.reset_num_unresolved(); visit(ctx_.root); if (is_final_pass()) { return pass_tracker_.get_num_passes(); } 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(); } return 1; } 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_arg(const Call &call, size_t index, const arg_type_spec &spec) { if (spec.skip_check) { return true; } return check_arg(call, spec.type, index, spec.literal); } bool SemanticAnalyser::check_arg(const Call &call, size_t index, const map_type_spec &spec) { if (auto *map = call.vargs.at(index).as()) { if (spec.type) { SizedType type = spec.type(call); assign_map_type(*map, type, &call); } if (is_final_pass() && map->type().IsNoneTy()) { map->addError() << "Undefined map: " + map->ident; } return true; } call.vargs.at(index).node().addError() << call.func << "() expects a map argument"; return false; } bool SemanticAnalyser::check_arg(const Call &call, size_t index, const map_key_spec &spec) { if (auto *map = call.vargs.at(spec.map_index).as()) { // This reconciles the argument if the other one is a map, but otherwise // we don't specifically emit an error. `map_type_spec` above does that. reconcile_map_key(map, call.vargs.at(index)); return true; } else { return false; } } bool SemanticAnalyser::check_call(const Call &call) { auto spec = CALL_SPEC.find(call.func); if (spec == CALL_SPEC.end()) { return true; } if (is_final_pass() && call.ret_val_discarded && spec->second.discard_ret_warn) { call.addWarning() << "Return value discarded for " << call.func << ". It should be used."; } auto ret = true; if (spec->second.min_args != spec->second.max_args) { ret = check_varargs(call, spec->second.min_args, spec->second.max_args); } else { ret = check_nargs(call, spec->second.min_args); } if (!ret) { return ret; } for (size_t i = 0; i < spec->second.arg_types.size() && i < call.vargs.size(); ++i) { std::visit([&](const auto &v) { ret = ret && check_arg(call, i, v); }, spec->second.arg_types.at(i)); } return ret; } // 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(); assert(nargs >= call.injected_args); assert(expected_nargs >= call.injected_args); nargs -= call.injected_args; expected_nargs -= call.injected_args; 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)"; call.addError() << 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(); assert(nargs >= call.injected_args); assert(min_nargs >= call.injected_args); assert(max_nargs >= call.injected_args); nargs -= call.injected_args; min_nargs -= call.injected_args; max_nargs -= call.injected_args; 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)"; call.addError() << 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)"; call.addError() << 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, size_t index, bool want_literal) { const auto &arg = call.vargs.at(index); bool is_literal = arg.is() || arg.is() || arg.is(); if (want_literal && (!is_literal || arg.type().GetTy() != type)) { call.addError() << 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 = arg.as(); if (pos_param) pos_param->addError() << "Use str($" << pos_param->n << ") to treat $" << pos_param->n << " as a string"; } return false; } else if (is_final_pass() && arg.type().GetTy() != type) { call.addError() << 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 = call.vargs.at(0).as(); if (!arg) { call.addError() << call.func << "() expects a string literal as the first argument"; return false; } std::string re = "^[a-zA-Z0-9./_-]+$"; bool is_valid = std::regex_match(arg->value, std::regex(re)); if (!is_valid) { call.addError() << call.func << "() expects a string that is a valid symbol (" << re << ") as input (\"" << arg << "\" provided)"; 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; } // Semantic analysis for assigning a value of the provided type to the given // map. The type within the passes `Map` node will be updated to reflect the // new type, if available. void SemanticAnalyser::assign_map_type(Map &map, const SizedType &type, const Node *loc_node, AssignMapStatement *assignment) { const std::string &map_ident = map.ident; if (type.IsRecordTy() && type.is_tparg) { loc_node->addError() << "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()) map.addError() << "Undefined map: " + map_ident; else *maptype = type; } else if (maptype->GetTy() != type.GetTy()) { loc_node->addError() << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << type << "' when map already contains a value of type '" << *maptype << "'"; } else if (maptype->IsSumTy() || maptype->IsMinTy() || maptype->IsMaxTy() || maptype->IsAvgTy() || maptype->IsStatsTy()) { if (maptype->IsSigned() != type.IsSigned()) { loc_node->addError() << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << type << "' when map already contains a value of type '" << *maptype << "'"; } } else if (maptype->IsIntegerTy() && !maptype->IsEqual(type)) { auto *integer = assignment ? assignment->expr.as() : nullptr; if (integer) { uint64_t value = integer->value; bool can_fit = false; if (!maptype->IsSigned()) { auto min_max = getUIntTypeRange(*maptype); can_fit = value <= min_max.second; } else { auto min_max = getIntTypeRange(*maptype); can_fit = value <= static_cast(min_max.second); } if (!can_fit) { loc_node->addError() << "Type mismatch for " << map_ident << ": " << "trying to assign value '" << static_cast(integer->value) << "' which does not fit into the map of type '" << *maptype << "'"; } } else if (maptype->IsSigned() != type.IsSigned()) { loc_node->addError() << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << type << "' when map already contains a value of type '" << *maptype << "'"; } } else if (maptype->IsStringTy() || maptype->IsTupleTy()) { update_string_size(*maptype, type); } map.value_type = *maptype; } 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); } map.value_type = map_val_[map_ident]; } } 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 = stmt.as(); if (jump && i < (stmts.size() - 1)) { jump->addWarning() << "All code after a '" << opstr(*jump) << "' is unreachable."; } } } } SizedType SemanticAnalyser::create_key_type(const SizedType &expr_type, Node &node) { 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, node); elements.push_back(std::move(keytype)); } new_key_type = CreateTuple(Struct::CreateTuple(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(expr_type.IsSigned()); new_key_type.SetIntBitWidth(64); } validate_map_key(new_key_type, node); 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 Expression &key_expr) { // 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.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)) { if (current_key_type.IsIntegerTy()) { auto *integer = key_expr.as(); if (integer) { uint64_t value = integer->value; bool can_fit = false; if (current_key_type.IsSigned()) { auto min_max = getIntTypeRange(current_key_type); can_fit = value <= static_cast(min_max.second); } else { auto min_max = getUIntTypeRange(current_key_type); can_fit = value <= min_max.second; } if (!can_fit) { key_expr.node().addError() << "Argument mismatch for " << map_ident << ": " << "trying to access with argument '" << static_cast(integer->value) << "' which does not fit into the map of key type '" << current_key_type << "'"; } } else if (current_key_type.IsSigned() != new_key_type.IsSigned()) { valid = false; } } else { valid = false; } } } else { valid = false; } if (valid) { return; } if (current_key_type.IsNoneTy()) { key_expr.node().addError() << "Argument mismatch for " << map_ident << ": " << "trying to access with arguments: '" << new_key_type << "' when map expects no arguments"; } else { key_expr.node().addError() << "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(Struct::CreateTuple(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(Struct::CreateTuple(new_elems)); } void SemanticAnalyser::resolve_struct_type(SizedType &type, Node &node) { 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()) { auto struct_type = bpftrace_.structs.Lookup(inner_type->GetName()).lock(); if (!struct_type) { node.addError() << "Cannot resolve unknown type \"" << inner_type->GetName() << "\"\n"; } else { type = CreateRecord(inner_type->GetName(), struct_type); while (pointer_level > 0) { type = CreatePointer(type); pointer_level--; } } } } Pass CreateSemanticPass(bool listing) { auto fn = [listing](ASTContext &ast, BPFtrace &b, CDefinitions &c_definitions, MapMetadata &mm, NamedParamDefaults &named_param_defaults, TypeMetadata &types) { SemanticAnalyser semantics(ast, b, c_definitions, mm, named_param_defaults, types, !b.cmd_.empty() || b.child_ != nullptr, listing); semantics.analyse(); }; return Pass::create("Semantic", fn); }; 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.24.1/src/ast/passes/semantic_analyser.h000066400000000000000000000002251506776124200221230ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateSemanticPass(bool listing = false); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/type_system.cpp000066400000000000000000000030711506776124200213440ustar00rootroot00000000000000#include #include "ast/ast.h" #include "ast/passes/clang_build.h" #include "ast/passes/type_system.h" namespace bpftrace::ast { Pass CreateTypeSystemPass() { auto fn = [](BitcodeModules &bm) -> Result { TypeMetadata result; // For now, we simply build a single type system that covers all the // external imports and standard library. In theory, this should be rebased // on top of the individual probe type system (coming from the kernel, // module or user binary). std::optional aggregate; for (const auto &s : bm.objects) { auto btf = btf::Types::parse(static_cast(s.data()), s.size()); if (!btf) { return btf.takeError(); } if (!aggregate) { aggregate.emplace(std::move(*btf)); } else { auto ok = aggregate->append(*btf); if (!ok) { return ok.takeError(); } } } if (aggregate) { result.global = std::move(*aggregate); } return result; }; return Pass::create("TypeSystem", fn); } Pass CreateDumpTypesPass(std::ostream &out) { auto fn = [&out](TypeMetadata &tm) { for (const auto &type : tm.global) { if (!type.is()) { continue; } out << type.as() << "\n"; } for (const auto &type : tm.global) { if (!type.is()) { continue; } out << type.as() << "\n"; } }; return Pass::create("DumpTypes", fn); } } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/type_system.h000066400000000000000000000014421506776124200210110ustar00rootroot00000000000000#pragma once #include #include "ast/ast.h" #include "ast/pass_manager.h" #include "btf/btf.h" namespace bpftrace::ast { // TypeMetadata contains metadata related to the set of external types that are // available to each probe. Note that this does not currently cover any of the // existing `SizedType` implementations. // // For now, this consistent of a single `global` set of types that are // available from the extern interop modules. In the future this may be // extended to "per-probe" types, e.g. the types loaded from the kernel, per // module or associated with user binaries. class TypeMetadata : public ast::State<"type-metadata"> { public: btf::Types global; }; Pass CreateTypeSystemPass(); Pass CreateDumpTypesPass(std::ostream &out); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/unstable_feature.cpp000066400000000000000000000112621506776124200223100ustar00rootroot00000000000000#include #include #include #include "ast/ast.h" #include "ast/passes/unstable_feature.h" #include "ast/visitor.h" #include "bpftrace.h" #include "config.h" #include "log.h" namespace bpftrace::ast { namespace { const auto MAP_DECL = "map declarations"; const auto IMPORTS = "imports"; const auto MACROS = "macros"; const auto TSERIES = "tseries"; const auto ADDR = "address-of operator (&)"; std::string get_warning(const std::string &feature, const std::string &config) { return std::string("Script is using an unstable feature: " + feature + ". To prevent this warning you must explicitly enable it " "in the config e.g. ") + config + std::string("=enable"); } std::string get_error(const std::string &feature, const std::string &config) { return std::string(feature + " feature is not enabled by default. To enable " "this unstable feature, set the config flag to enable. ") + config + std::string("=enable"); } class UnstableFeature : public Visitor { public: explicit UnstableFeature(BPFtrace &bpftrace, std::unordered_set macros) : bpftrace_(bpftrace), macros(std::move(macros)) {}; using Visitor::visit; void visit(MapDeclStatement &decl); void visit(MapAddr &map_addr); void visit(VariableAddr &var_addr); void visit(Import &imp); void visit(Call &call); private: BPFtrace &bpftrace_; // This set is so we don't warn multiple times for the same feature. std::unordered_set warned_features; std::unordered_set macros; void check_unstable_addr(Node &node); }; } // namespace // Note: for logged warnings we don't want to use the AST node's `addWarning()` // as this also prints the code location which is overly noisy. We just // want to notify users they're using an unstable feature. For errors it's ok to // print the location because the script is going to fail anyway. void UnstableFeature::visit(MapDeclStatement &decl) { if (bpftrace_.config_->unstable_map_decl == ConfigUnstable::error) { decl.addError() << get_error(MAP_DECL, UNSTABLE_MAP_DECL); return; } if (bpftrace_.config_->unstable_map_decl == ConfigUnstable::warn && !warned_features.contains(UNSTABLE_MAP_DECL)) { LOG(WARNING) << get_warning(MAP_DECL, UNSTABLE_MAP_DECL); warned_features.insert(UNSTABLE_MAP_DECL); } } void UnstableFeature::visit(Import &imp) { if (bpftrace_.config_->unstable_import == ConfigUnstable::error) { imp.addError() << get_error(IMPORTS, UNSTABLE_IMPORT); return; } if (bpftrace_.config_->unstable_import == ConfigUnstable::warn && !warned_features.contains(UNSTABLE_IMPORT)) { LOG(WARNING) << get_warning(IMPORTS, UNSTABLE_IMPORT); warned_features.insert(UNSTABLE_IMPORT); } } void UnstableFeature::visit(Call &call) { if (macros.contains(call.func)) { if (bpftrace_.config_->unstable_macro == ConfigUnstable::error) { call.addError() << get_error(MACROS, UNSTABLE_MACRO); return; } if (bpftrace_.config_->unstable_macro == ConfigUnstable::warn && !warned_features.contains(UNSTABLE_MACRO)) { LOG(WARNING) << get_warning(MACROS, UNSTABLE_MACRO); warned_features.insert(UNSTABLE_MACRO); } return; } if (call.func != "tseries") { return; } if (bpftrace_.config_->unstable_tseries == ConfigUnstable::error) { call.addError() << get_error(TSERIES, UNSTABLE_TSERIES); return; } if (bpftrace_.config_->unstable_tseries == ConfigUnstable::warn && !warned_features.contains(UNSTABLE_TSERIES)) { LOG(WARNING) << get_warning(TSERIES, UNSTABLE_TSERIES); warned_features.insert(UNSTABLE_TSERIES); } } void UnstableFeature::check_unstable_addr(Node &node) { if (bpftrace_.config_->unstable_addr == ConfigUnstable::error) { node.addError() << get_error(ADDR, UNSTABLE_ADDR); return; } if (bpftrace_.config_->unstable_addr == ConfigUnstable::warn && !warned_features.contains(UNSTABLE_ADDR)) { LOG(WARNING) << get_warning(ADDR, UNSTABLE_ADDR); warned_features.insert(UNSTABLE_ADDR); } } void UnstableFeature::visit(MapAddr &map_addr) { check_unstable_addr(map_addr); } void UnstableFeature::visit(VariableAddr &var_addr) { check_unstable_addr(var_addr); } Pass CreateUnstableFeaturePass() { return Pass::create("UnstableFeature", [](ASTContext &ast, BPFtrace &b) { std::unordered_set macros; for (Macro *macro : ast.root->macros) { macros.insert(macro->name); } auto configs = UnstableFeature(b, std::move(macros)); configs.visit(ast.root); }); }; } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/passes/unstable_feature.h000066400000000000000000000002101506776124200217440ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" namespace bpftrace::ast { Pass CreateUnstableFeaturePass(); } // namespace bpftrace::ast bpftrace-0.24.1/src/ast/signal.cpp000066400000000000000000000023271506776124200167410ustar00rootroot00000000000000#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::ranges::for_each(sig, [](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.24.1/src/ast/signal_bt.h000066400000000000000000000003341506776124200170670ustar00rootroot00000000000000// 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.24.1/src/ast/visitor.h000066400000000000000000000165151506776124200166340ustar00rootroot00000000000000#pragma once #include #include #include "ast/ast.h" #include "ast/context.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. To replace specific types, // a `visit` method must be provided on the suitable dynamic type (e.g. you // may want `Expression` or `Statement`). template class Visitor { public: // 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([[maybe_unused]] Integer &integer) { return default_value(); } R visit([[maybe_unused]] NegativeInteger &integer) { return default_value(); } R visit([[maybe_unused]] Boolean &boolean) { return default_value(); } R visit([[maybe_unused]] PositionalParameter ¶m) { return default_value(); } R visit([[maybe_unused]] PositionalParameterCount ¶m) { return default_value(); } R visit([[maybe_unused]] String &string) { return default_value(); } R visit([[maybe_unused]] Builtin &builtin) { return default_value(); } R visit([[maybe_unused]] Identifier &identifier) { return default_value(); } R visit([[maybe_unused]] Variable &var) { return default_value(); } R visit([[maybe_unused]] VariableAddr &var_addr) { return visitImpl(var_addr.var); } R visit([[maybe_unused]] SubprogArg &subprog_arg) { return default_value(); } R visit([[maybe_unused]] AttachPoint &ap) { return default_value(); } R visit(Call &call) { return visitImpl(call.vargs); } R visit(Sizeof &szof) { return visitImpl(szof.record); } R visit([[maybe_unused]] Offsetof &ofof) { return visitImpl(ofof.record); } R visit([[maybe_unused]] MapDeclStatement &decl) { return default_value(); } R visit([[maybe_unused]] Map &map) { return default_value(); } R visit([[maybe_unused]] MapAddr &map_addr) { return visitImpl(map_addr.map); } R visit(Binop &binop) { visitImpl(binop.left); visitImpl(binop.right); return default_value(); } R visit(Unop &unop) { return visitImpl(unop.expr); } R visit(Ternary &ternary) { visitImpl(ternary.cond); visitImpl(ternary.left); visitImpl(ternary.right); return default_value(); } R visit(FieldAccess &acc) { return visitImpl(acc.expr); } R visit(ArrayAccess &arr) { visitImpl(arr.expr); visitImpl(arr.indexpr); return default_value(); } R visit(TupleAccess &acc) { return visitImpl(acc.expr); } R visit(MapAccess &acc) { visitImpl(acc.map); visitImpl(acc.key); return default_value(); } R visit(Cast &cast) { return visitImpl(cast.expr); } R visit(Tuple &tuple) { return visitImpl(tuple.elems); } R visit(ExprStatement &expr) { return visitImpl(expr.expr); } R visit(AssignScalarMapStatement &assignment) { visitImpl(assignment.map); visitImpl(assignment.expr); return default_value(); } R visit(AssignMapStatement &assignment) { visitImpl(assignment.map); visitImpl(assignment.key); visitImpl(assignment.expr); return default_value(); } R visit(AssignVarStatement &assignment) { visitImpl(assignment.var_decl); visitImpl(assignment.expr); return default_value(); } R visit([[maybe_unused]] AssignConfigVarStatement &assignment) { return default_value(); } R visit(VarDeclStatement &decl) { return visitImpl(decl.var); } R visit(If &if_node) { visitImpl(if_node.cond); visitImpl(if_node.if_block); visitImpl(if_node.else_block); return default_value(); } R visit(Jump &jump) { return visitImpl(jump.return_value); } R visit(Unroll &unroll) { visitImpl(unroll.expr); visitImpl(unroll.block); return default_value(); } R visit(While &while_block) { visitImpl(while_block.cond); visitImpl(while_block.block); return default_value(); } R visit(Range &range) { visitImpl(range.start); visitImpl(range.end); return default_value(); } R visit(For &for_loop) { visitImpl(for_loop.decl); visitImpl(for_loop.iterable); visitImpl(for_loop.stmts); return default_value(); } R visit(Predicate &pred) { return visitImpl(pred.expr); } R visit(Probe &probe) { visitImpl(probe.attach_points); visitImpl(probe.pred); visitImpl(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(BlockExpr &block_expr) { visitImpl(block_expr.stmts); visitImpl(block_expr.expr); return default_value(); } R visit([[maybe_unused]] Macro ¯o) { // In general because macros are expanded in an early pass (macro_expansion) // later passes shouldn't visit any macros; visitation should be specially // handled by the macro_expansion pass. return default_value(); } R visit(Subprog &subprog) { visitImpl(subprog.args); visitImpl(subprog.stmts); return default_value(); } R visit([[maybe_unused]] Import &imp) { return default_value(); } R visit(Program &program) { // This order is important. visitImpl(program.config); visitImpl(program.imports); visitImpl(program.macros); visitImpl(program.functions); visitImpl(program.map_decls); visitImpl(program.probes); return default_value(); } R visit(Iterable &iterable) { return visitImpl(iterable.value); } R visit(Expression &expr) { return visitImpl(expr.value); } R visit(Statement &stmt) { return visitImpl(stmt.value); } R visit([[maybe_unused]] const SizedType &type) { return default_value(); } // 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([&](auto &v) -> R { return visitImpl(v); }, var); } template R visit(std::vector &var) { for (auto &value : var) { visitImpl(value); } return default_value(); } template R visit(std::optional &var) { if (var.has_value()) { return visitImpl(var.value()); } return default_value(); } 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); } R default_value() { if constexpr (!std::is_void_v) { return R(); } } }; } // namespace bpftrace::ast bpftrace-0.24.1/src/async_action.cpp000066400000000000000000000217301506776124200173460ustar00rootroot00000000000000#include #include #include "ast/async_event_types.h" #include "async_action.h" #include "bpftrace.h" #include "log.h" #include "types_format.h" #include "util/exceptions.h" #include "util/io.h" #include "util/system.h" namespace bpftrace::async_action { static Result> prepare_args( BPFtrace &bpftrace, const ast::CDefinitions &c_definitions, const std::vector &fields, const OpaqueValue &value) { std::vector res; for (const auto &field : fields) { auto v = format(bpftrace, c_definitions, field.type, value.slice(field.offset, field.type.GetSize())); if (!v) { return v.takeError(); } res.emplace_back(std::move(*v)); } return res; } void AsyncHandlers::exit(const OpaqueValue &data) { auto exit = data.bitcast(); BPFtrace::exit_code = exit.exit_code; bpftrace.request_finalize(); } void AsyncHandlers::join(const OpaqueValue &data) { auto join = data.bitcast(); uint64_t join_id = join.join_id; const auto *delim = bpftrace.resources.join_args[join_id].c_str(); auto arg = data.slice(sizeof(AsyncEvent::Join)); size_t arg_count = arg.count() / bpftrace.join_argsize_; std::stringstream joined; for (unsigned int i = 0; i < arg_count; i++) { if (i) joined << delim; joined << (arg.data() + (i * bpftrace.join_argsize_)); } out.join(joined.str()); } void AsyncHandlers::time(const OpaqueValue &data) { // not respecting config_->get(ConfigKeyInt::max_strlen) char timestr[AsyncHandlers::MAX_TIME_STR_LEN]; time_t t; struct tm tmp; t = ::time(nullptr); if (!localtime_r(&t, &tmp)) { LOG(WARNING) << "localtime_r: " << strerror(errno); return; } auto time = data.bitcast(); const 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; } out.time(timestr); } void AsyncHandlers::runtime_error(const OpaqueValue &data) { auto runtime_error = data.bitcast(); auto error_id = runtime_error.error_id; const auto return_value = runtime_error.return_value; const auto &info = bpftrace.resources.runtime_error_info[error_id]; out.runtime_error(return_value, info); } void AsyncHandlers::print_non_map(const OpaqueValue &data) { auto print = data.bitcast(); const SizedType &ty = bpftrace.resources.non_map_print_args.at( print.print_id); auto v = format( bpftrace, c_definitions, ty, data.slice(sizeof(AsyncEvent::PrintNonMap))); if (!v) { LOG(BUG) << "error printing non-map value: " << v.takeError(); } out.value(*v); } void AsyncHandlers::print_map(const OpaqueValue &data) { auto print = data.bitcast(); const auto &map = bpftrace.bytecode_.getMap(print.mapid); auto res = format(bpftrace, c_definitions, map, print.top, print.div); if (!res) { LOG(BUG) << "Could not print map with ident \"" << map.name() << "\": " << res.takeError(); } out.map(map.name(), *res); } void AsyncHandlers::zero_map(const OpaqueValue &data) { auto mapevent = data.bitcast(); const auto &map = bpftrace.bytecode_.getMap(mapevent.mapid); uint64_t nvalues = map.is_per_cpu_type() ? bpftrace.ncpus_ : 1; auto ok = map.zero_out(nvalues); if (!ok) { LOG(BUG) << "Could not zero map with ident \"" << map.name() << "\", err=" << ok.takeError(); } } void AsyncHandlers::clear_map(const OpaqueValue &data) { auto mapevent = data.bitcast(); const auto &map = bpftrace.bytecode_.getMap(mapevent.mapid); uint64_t nvalues = map.is_per_cpu_type() ? bpftrace.ncpus_ : 1; auto ok = map.clear(nvalues); if (!ok) { LOG(BUG) << "Could not clear map with ident \"" << map.name() << "\", err=" << ok.takeError(); } } void AsyncHandlers::watchpoint_attach(const OpaqueValue &data) { auto watchpoint = data.bitcast(); uint64_t probe_idx = watchpoint.watchpoint_idx; uint64_t addr = watchpoint.addr; if (probe_idx >= bpftrace.resources.watchpoint_probes.size()) { LOG(BUG) << "Invalid watchpoint probe idx=" << probe_idx; } // 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 { Probe &wp_probe = bpftrace.resources.watchpoint_probes[probe_idx]; wp_probe.address = addr; auto ap = bpftrace.attach_probe(wp_probe, bpftrace.bytecode_); if (!ap) { if (bpftrace.config_->missing_probes == ConfigMissingProbes::error) { throw util::FatalUserException( "Unable to attach real watchpoint probe"); } } else { bpftrace.attached_probes_.push_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 || bpftrace.resume_tracee(pid) != 0) { throw util::FatalUserException( "Failed to SIGCONT tracee (pid: " + std::to_string(pid) + "): " + strerror(errno)); } } void AsyncHandlers::watchpoint_detach(const OpaqueValue &data) { auto unwatch = data.bitcast(); 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. auto it = std::ranges::remove_if(bpftrace.attached_probes_, [&](const auto &ap) { return ap->probe().address == addr; }); bpftrace.attached_probes_.erase(it.begin(), it.end()); } void AsyncHandlers::skboutput(const OpaqueValue &data) { auto hdr = data.bitcast(); int offset = std::get<1>( bpftrace.resources.skboutput_args_.at(hdr.skb_output_id)); auto pkt = data.slice(sizeof(hdr)); if (static_cast(offset) >= pkt.size()) { return; // Nothing to dump. } bpftrace.write_pcaps(hdr.skb_output_id, hdr.nsecs_since_boot, pkt.slice(offset)); } void AsyncHandlers::syscall(const OpaqueValue &data) { if (bpftrace.safe_mode_) { throw util::FatalUserException( "syscall() not allowed in safe mode. Use '--unsafe'."); } auto id = data.bitcast() - static_cast(AsyncAction::syscall); auto &fmt = std::get<0>(bpftrace.resources.system_args[id]); auto &args = std::get<1>(bpftrace.resources.system_args[id]); auto vals = prepare_args( bpftrace, c_definitions, args, data.slice(sizeof(uint64_t))); if (!vals) { LOG(BUG) << "Error processing syscall arguments: " << vals.takeError(); } auto result = util::exec_system(fmt.format(*vals).c_str()); if (!result) { LOG(ERROR) << "Error executing program: " << result.takeError(); return; } out.syscall(*result); } void AsyncHandlers::cat(const OpaqueValue &data) { auto id = data.bitcast() - static_cast(AsyncAction::cat); auto &fmt = std::get<0>(bpftrace.resources.cat_args[id]); auto &args = std::get<1>(bpftrace.resources.cat_args[id]); auto vals = prepare_args( bpftrace, c_definitions, args, data.slice(sizeof(uint64_t))); if (!vals) { LOG(BUG) << "Error processing cat arguments: " << vals.takeError(); } std::stringstream buf; util::cat_file(fmt.format(*vals).c_str(), bpftrace.config_->max_cat_bytes, buf); out.cat(buf.str()); } void AsyncHandlers::printf(const OpaqueValue &data) { auto id = data.bitcast() - static_cast(AsyncAction::printf); auto &fmt = std::get<0>(bpftrace.resources.printf_args[id]); auto &args = std::get<1>(bpftrace.resources.printf_args[id]); auto severity = std::get<2>(bpftrace.resources.printf_args[id]); auto &source_info = std::get<3>(bpftrace.resources.printf_args[id]); auto vals = prepare_args( bpftrace, c_definitions, args, data.slice(sizeof(uint64_t))); if (!vals) { LOG(BUG) << "Error processing printf arguments: " << vals.takeError(); } switch (severity) { case PrintfSeverity::NONE: { out.printf(fmt.format(*vals)); return; } case PrintfSeverity::ERROR: { out.errorf(fmt.format(*vals), source_info); return; } } } } // namespace bpftrace::async_action bpftrace-0.24.1/src/async_action.h000066400000000000000000000031751506776124200170160ustar00rootroot00000000000000#pragma once #include "ast/async_event_types.h" #include "bpftrace.h" #include "output/output.h" namespace bpftrace::async_action { enum class AsyncAction { // clang-format off printf = 0, // printf reserves 0-9999 for printf_ids printf_end = 9999, syscall = 10000, // system reserves 10000-19999 for printf_ids syscall_end = 19999, cat = 20000, // cat reserves 20000-29999 for printf_ids cat_end = 29999, exit = 30000, print, clear, zero, time, join, runtime_error, print_non_map, strftime, watchpoint_attach, watchpoint_detach, skboutput, // clang-format on }; class AsyncHandlers { public: const static size_t MAX_TIME_STR_LEN = 64; AsyncHandlers(BPFtrace &bpftrace, const ast::CDefinitions &c_definitions, output::Output &output) : bpftrace(bpftrace), c_definitions(c_definitions), out(output) {}; void exit(const OpaqueValue &data); void join(const OpaqueValue &data); void time(const OpaqueValue &data); void runtime_error(const OpaqueValue &data); void print_non_map(const OpaqueValue &data); void print_map(const OpaqueValue &data); void zero_map(const OpaqueValue &data); void clear_map(const OpaqueValue &data); void watchpoint_attach(const OpaqueValue &data); void watchpoint_detach(const OpaqueValue &data); void skboutput(const OpaqueValue &data); void syscall(const OpaqueValue &data); void cat(const OpaqueValue &data); void printf(const OpaqueValue &data); private: BPFtrace &bpftrace; const ast::CDefinitions &c_definitions; output::Output &out; }; } // namespace bpftrace::async_action bpftrace-0.24.1/src/attached_probe.cpp000066400000000000000000001357741506776124200176560ustar00rootroot00000000000000#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 "usdt.h" #include "util/bpf_names.h" #include "util/cpus.h" #include "util/exceptions.h" #include "util/kernel.h" #include "util/symbols.h" namespace bpftrace { char AttachError::ID; void AttachError::log(llvm::raw_ostream &OS) const { OS << msg_; } 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::benchmark: 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::benchmark: return libbpf::BPF_PROG_TYPE_XDP; 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_TRACING; 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; } } std::string eventprefix(ProbeType t) { switch (attachtype(t)) { case BPF_PROBE_ENTRY: return "p_"; case BPF_PROBE_RETURN: return "r_"; } return {}; // unreached } std::string eventname(const Probe &probe, uint64_t offset) { 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(probe.type) + util::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(probe.type) + util::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 << "\""; } } Result resolve_offset_kprobe(Probe &probe) { uint64_t 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 offset; // 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) { return make_error("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 offset; } // 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) { return make_error("Offset outside the function bounds ('" + probe.attach_point + "' size is " + std::to_string(sym.size) + ")"); } return offset; } Result resolve_offset(Probe &probe) { bcc_symbol bcc_sym; if (bcc_resolve_symname(probe.path.c_str(), probe.attach_point.c_str(), probe.loc, 0, nullptr, &bcc_sym)) { return make_error("Could not resolve symbol: " + probe.path + ":" + probe.attach_point); } // 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."; Result<> check_alignment(Probe &probe, std::string &symbol, uint64_t sym_offset, uint64_t func_offset, bool safe_mode) { Disasm dasm(probe.path); AlignState aligned = dasm.is_aligned(sym_offset, func_offset); std::string tmp = probe.path + ":" + symbol + "+" + std::to_string(func_offset); switch (aligned) { case AlignState::Ok: return OK(); case AlignState::NotAlign: if (safe_mode) { return make_error( "Could not add " + probetypeName(probe.type) + " into middle of instruction: " + tmp + std::string{ hint_unsafe }); } else { std::string_view hint; LOG(WARNING) << "Unsafe " << probe.type << " in the middle of the instruction: " << tmp << hint; return OK(); } case AlignState::Fail: if (safe_mode) { return make_error( "Failed to check if " + probetypeName(probe.type) + " is in proper place: " + tmp + std::string{ hint_unsafe }); } else { LOG(WARNING) << "Unchecked " << probe.type << ": " << tmp; return OK(); } case AlignState::NotSupp: if (safe_mode) { return make_error("Can't check if " + probetypeName(probe.type) + " is in proper place (compiled without " "(k|u)probe offset support): " + tmp + std::string{ hint_unsafe }); } else { LOG(WARNING) << "Unchecked " << probe.type << ": " << tmp; return OK(); } } return OK(); } Result resolve_offset_uprobe(Probe &probe, bool safe_mode) { 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(), util::sym_address_cb, &option, &sym); if (!sym.start) { if (safe_mode) { std::stringstream ss; ss << "0x" << std::hex << probe.address; return make_error( "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!"; return probe.address; } } symbol = sym.name; func_offset = probe.address - sym.start; } else { sym.name = symbol; bcc_elf_foreach_sym(probe.path.c_str(), util::sym_name_cb, &option, &sym); if (!sym.start) { return make_error("Could not resolve symbol: " + probe.path + ":" + symbol); } } if (probe.type == ProbeType::uretprobe && func_offset != 0) { return make_error("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; return make_error(msg.str()); } else { LOG(WARNING) << msg.str() << " Skipping attachment." << hint_unsafe; } return make_error(); } } else if (func_offset >= sym.size) { return make_error("Offset outside the function bounds ('" + symbol + "' size is " + std::to_string(sym.size) + ")"); } auto sym_offset = resolve_offset(probe); if (!sym_offset) { return sym_offset.takeError(); } uint64_t 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 offset; auto align_ok = check_alignment( probe, symbol, *sym_offset, func_offset, safe_mode); if (!align_ok) { return align_ok.takeError(); } return offset; } #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 /*unused*/, void *p) { auto *data = static_cast(p); std::vector &syms = data->syms; if (std::ranges::binary_search(syms, 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) { auto *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; } Result> resolve_offsets_uprobe_multi( Probe &probe, std::vector &syms) { std::vector offsets; struct bcc_symbol_option option = {}; int err; // Parse symbols names into syms vector for (const std::string &func : probe.funcs) { auto pos = func.find(':'); if (pos == std::string::npos) { return make_error("Error resolving probe: " + probe.name); } syms.push_back(func.substr(pos + 1)); } std::ranges::sort(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(probe.path.c_str(), bcc_sym_cb, &option, &data); if (err) { return make_error("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(probe.path.c_str(), bcc_load_cb, &addrs); if (err) { return make_error( "Failed to resolve symbols offsets for probe: " + probe.name); } for (auto a : addrs) { offsets.push_back(a.offset); } return offsets; } #endif // HAVE_LIBBPF_UPROBE_MULTI class AttachedKprobeProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, BPFtrace &bpftrace); ~AttachedKprobeProbe() override; int link_fd() override; private: AttachedKprobeProbe(const Probe &probe, struct bpf_link *link); struct bpf_link *link_; }; AttachedKprobeProbe::AttachedKprobeProbe(const Probe &probe, struct bpf_link *link) : AttachedProbe(probe), link_(link) { } AttachedKprobeProbe::~AttachedKprobeProbe() { if (bpf_link__destroy(link_)) { LOG(WARNING) << "failed to destroy link for kprobe probe: " << strerror(errno); } } int AttachedKprobeProbe::link_fd() { return bpf_link__fd(link_); } Result> AttachedKprobeProbe::make( Probe &probe, const BpfProgram &prog, BPFtrace &bpftrace) { if ((!probe.attach_point.empty() || probe.address != 0) && !bpftrace.is_traceable_func(probe.attach_point)) return make_error(); // 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.empty()) && modname != "vmlinux") { if (!util::is_module_loaded(modname)) { return make_error("specified module " + modname + " in probe " + probe.name + " is not loaded."); } funcname = modname + ":" + funcname; } // 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(); auto offset_res = resolve_offset_kprobe(probe); if (!offset_res) { return offset_res.takeError(); } uint64_t offset = *offset_res; if (!is_symbol_kprobe) funcname += probe.attach_point; BPFTRACE_LIBBPF_OPTS(bpf_kprobe_opts, opts, .offset = offset, .retprobe = probe.type == ProbeType::kretprobe); auto *link = bpf_program__attach_kprobe_opts(prog.bpf_prog(), funcname.c_str(), &opts); if (!link) { if (errno == EILSEQ) return make_error( "Possible attachment attempt in the middle of an instruction, " "try a different offset."); return make_error(); } return std::unique_ptr( new AttachedKprobeProbe(probe, link)); } class AttachedMultiKprobeProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog); ~AttachedMultiKprobeProbe() override; int link_fd() override; size_t probe_count() const override; private: AttachedMultiKprobeProbe(const Probe &probe, int link_fd); int link_fd_; }; AttachedMultiKprobeProbe::AttachedMultiKprobeProbe(const Probe &probe, int link_fd) : AttachedProbe(probe), link_fd_(link_fd) { } AttachedMultiKprobeProbe::~AttachedMultiKprobeProbe() { close(link_fd_); } int AttachedMultiKprobeProbe::link_fd() { return link_fd_; } size_t AttachedMultiKprobeProbe::probe_count() const { return probe_.funcs.size(); } Result> AttachedMultiKprobeProbe:: make(Probe &probe, const BpfProgram &prog) { BPFTRACE_LIBBPF_OPTS(bpf_link_create_opts, opts); std::vector syms; unsigned int i = 0; for (const auto &func : probe.funcs) syms.push_back(func.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]; } } auto attach_type = probe.is_session ? libbpf::BPF_TRACE_KPROBE_SESSION : libbpf::BPF_TRACE_KPROBE_MULTI; int link_fd = bpf_link_create( prog.fd(), 0, static_cast(attach_type), &opts); if (link_fd < 0) { return make_error(); } return std::unique_ptr( new AttachedMultiKprobeProbe(probe, link_fd)); } class AttachedUprobeProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid, bool safe_mode); ~AttachedUprobeProbe() override; int link_fd() override; private: AttachedUprobeProbe(const Probe &probe, struct bpf_link *link); struct bpf_link *link_; }; AttachedUprobeProbe::AttachedUprobeProbe(const Probe &probe, struct bpf_link *link) : AttachedProbe(probe), link_(link) { } AttachedUprobeProbe::~AttachedUprobeProbe() { if (bpf_link__destroy(link_)) LOG(WARNING) << "failed to destroy link for uprobe probe: " << strerror(errno); } int AttachedUprobeProbe::link_fd() { return bpf_link__fd(link_); } Result> AttachedUprobeProbe::make( Probe &probe, const BpfProgram &prog, std::optional pid, bool safe_mode) { auto offset_res = resolve_offset_uprobe(probe, safe_mode); if (!offset_res) { return offset_res.takeError(); } BPFTRACE_LIBBPF_OPTS(bpf_uprobe_opts, opts, .retprobe = probe.type == ProbeType::uretprobe); auto *link = bpf_program__attach_uprobe_opts(prog.bpf_prog(), pid.has_value() ? *pid : -1, probe.path.c_str(), *offset_res, &opts); if (!link) { return make_error(); } return std::unique_ptr( new AttachedUprobeProbe(probe, link)); } class AttachedMultiUprobeProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid); ~AttachedMultiUprobeProbe() override; size_t probe_count() const override; private: AttachedMultiUprobeProbe(const Probe &probe, int link_fd); int link_fd_; }; AttachedMultiUprobeProbe::AttachedMultiUprobeProbe(const Probe &probe, int link_fd) : AttachedProbe(probe), link_fd_(link_fd) { } AttachedMultiUprobeProbe::~AttachedMultiUprobeProbe() { close(link_fd_); } size_t AttachedMultiUprobeProbe::probe_count() const { return probe_.funcs.size(); } #ifdef HAVE_LIBBPF_UPROBE_MULTI Result> AttachedMultiUprobeProbe:: make(Probe &probe, const BpfProgram &prog, std::optional pid) { std::vector syms; unsigned int i; // Resolve probe_.funcs into offsets and syms vector auto offset_res = resolve_offsets_uprobe_multi(probe, syms); if (!offset_res) { return offset_res.takeError(); } // 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 = offset_res->data(); opts.uprobe_multi.cnt = offset_res->size(); opts.uprobe_multi.flags = probe.type == ProbeType::uretprobe ? BPF_F_UPROBE_MULTI_RETURN : 0; if (pid.has_value()) { 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]; } } int link_fd = bpf_link_create(prog.fd(), 0, static_cast( libbpf::BPF_TRACE_UPROBE_MULTI), &opts); if (link_fd < 0) { return make_error(); } return std::unique_ptr( new AttachedMultiUprobeProbe(probe, link_fd)); } #else Result> AttachedMultiUprobeProbe:: make(Probe &probe, const BpfProgram &prog, std::optional pid) { return make_error("uprobe multi not available on this system"); } #endif // HAVE_LIBBPF_UPROBE_MULTI class AttachedUSDTProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid); ~AttachedUSDTProbe() override; private: AttachedUSDTProbe(const Probe &probe, int perf_event_fd); int perf_event_fd_; }; AttachedUSDTProbe::AttachedUSDTProbe(const Probe &probe, int perf_event_fd) : AttachedProbe(probe), perf_event_fd_(perf_event_fd) { } AttachedUSDTProbe::~AttachedUSDTProbe() { if (bpf_close_perf_event_fd(perf_event_fd_)) LOG(WARNING) << "failed to close perf event FD for usdt probe"; } Result> AttachedUSDTProbe::make( Probe &probe, const BpfProgram &prog, std::optional pid) { 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.has_value()) { if (!probe.path.empty()) { auto real_path = std::filesystem::absolute(probe.path).string(); ctx = bcc_usdt_new_frompid(*pid, real_path.c_str()); } else { ctx = bcc_usdt_new_frompid(*pid, nullptr); } if (!ctx) { return make_error( "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) { return make_error("Error initializing context for probe: " + probe.name); } } // Resolve location of usdt probe USDTHelper usdt_helper; auto u = usdt_helper.find(pid, probe.path, probe.ns, probe.attach_point); if (!u.has_value()) { return make_error("Failed to find usdt probe: " + eventname(probe, 0)); } 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) { return make_error("Error finding location for probe: " + probe.name); } probe.loc = loc.address; auto offset_res = resolve_offset(probe); if (!offset_res) { return offset_res.takeError(); } uint64_t offset = *offset_res; // 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); bcc_usdt_close(ctx); int perf_event_fd = bpf_attach_uprobe(prog.fd(), attachtype(probe.type), eventname(probe, offset).c_str(), probe.path.c_str(), offset, pid.has_value() ? *pid : -1, semaphore_offset); if (perf_event_fd < 0) { if (pid.has_value()) return make_error("Does PID exist? PID: " + std::to_string(*pid)); return make_error(); } return std::unique_ptr( new AttachedUSDTProbe(probe, perf_event_fd)); } class AttachedTracepointProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog); ~AttachedTracepointProbe() override; private: AttachedTracepointProbe(const Probe &probe, struct bpf_link *link); struct bpf_link *link_; }; AttachedTracepointProbe::AttachedTracepointProbe(const Probe &probe, struct bpf_link *link) : AttachedProbe(probe), link_(link) { } AttachedTracepointProbe::~AttachedTracepointProbe() { if (bpf_link__destroy(link_)) { LOG(WARNING) << "failed to destroy link for tracepiont probe: " << strerror(errno); } } Result> AttachedTracepointProbe::make( Probe &probe, const BpfProgram &prog) { auto *link = bpf_program__attach_tracepoint(prog.bpf_prog(), probe.path.c_str(), eventname(probe, 0).c_str()); if (!link) { return make_error(); } return std::unique_ptr( new AttachedTracepointProbe(probe, link)); } int open_perf_event(uint32_t ev_type, uint32_t ev_config, uint64_t sample_period, uint64_t sample_freq, pid_t pid, int cpu, int group_fd) { if (sample_period > 0 && sample_freq > 0) { LOG(BUG) << "Exactly one of sample_period / sample_freq should be set"; return -1; } struct perf_event_attr attr = {}; attr.type = ev_type; attr.size = sizeof(struct perf_event_attr); attr.config = ev_config; if (sample_freq > 0) { attr.freq = 1; attr.sample_freq = sample_freq; } else { attr.sample_period = sample_period; } if (pid > 0) attr.inherit = 1; return syscall( __NR_perf_event_open, &attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC); } class AttachedProfileProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid); ~AttachedProfileProbe() override; private: AttachedProfileProbe(const Probe &probe, std::vector links); std::vector links_; }; AttachedProfileProbe::AttachedProfileProbe(const Probe &probe, std::vector links) : AttachedProbe(probe), links_(std::move(links)) { } AttachedProfileProbe::~AttachedProfileProbe() { for (struct bpf_link *link : links_) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for profile probe: " << strerror(errno); } } Result> AttachedProfileProbe::make( Probe &probe, const BpfProgram &prog, std::optional pid) { 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 { return make_error("invalid profile path \"" + probe.path + "\""); } bool has_error = false; std::vector links; std::vector cpus = util::get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = open_perf_event(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, period, freq, pid.has_value() ? *pid : -1, cpu, group_fd); if (perf_event_fd < 0) { has_error = true; break; } auto *link = bpf_program__attach_perf_event(prog.bpf_prog(), perf_event_fd); if (!link) { has_error = true; break; } links.push_back(link); } if (has_error) { for (struct bpf_link *link : links) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for profile probe: " << strerror(errno); } return make_error(); } return std::unique_ptr( new AttachedProfileProbe(probe, links)); } class AttachedIntervalProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog); ~AttachedIntervalProbe() override; int link_fd() override; private: AttachedIntervalProbe(const Probe &probe, struct bpf_link *link); struct bpf_link *link_; }; AttachedIntervalProbe::AttachedIntervalProbe(const Probe &probe, struct bpf_link *link) : AttachedProbe(probe), link_(link) { } AttachedIntervalProbe::~AttachedIntervalProbe() { if (bpf_link__destroy(link_)) LOG(WARNING) << "failed to destroy link for interval probe: " << strerror(errno); } int AttachedIntervalProbe::link_fd() { return bpf_link__fd(link_); } Result> AttachedIntervalProbe::make( Probe &probe, const BpfProgram &prog) { 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 { return make_error("invalid interval path \"" + probe.path + "\""); } int perf_event_fd = open_perf_event(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, period, freq, -1, cpu, group_fd); if (perf_event_fd < 0) { return make_error(); } auto *link = bpf_program__attach_perf_event(prog.bpf_prog(), perf_event_fd); if (!link) { return make_error(); } return std::unique_ptr( new AttachedIntervalProbe(probe, link)); } class AttachedSoftwareProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid); ~AttachedSoftwareProbe() override; private: AttachedSoftwareProbe(const Probe &probe, std::vector links); std::vector links_; }; AttachedSoftwareProbe::AttachedSoftwareProbe( const Probe &probe, std::vector links) : AttachedProbe(probe), links_(std::move(links)) { } AttachedSoftwareProbe::~AttachedSoftwareProbe() { for (struct bpf_link *link : links_) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for software probe: " << strerror(errno); } } Result> AttachedSoftwareProbe::make( Probe &probe, const BpfProgram &prog, std::optional pid) { 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 (const 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; bool has_error = false; std::vector links; std::vector cpus = util::get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = open_perf_event(PERF_TYPE_SOFTWARE, type, period, 0, pid.has_value() ? *pid : -1, cpu, group_fd); if (perf_event_fd < 0) { has_error = true; break; } auto *link = bpf_program__attach_perf_event(prog.bpf_prog(), perf_event_fd); if (!link) { has_error = true; break; } links.push_back(link); } if (has_error) { for (struct bpf_link *link : links) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for software probe: " << strerror(errno); } return make_error(); } return std::unique_ptr( new AttachedSoftwareProbe(probe, links)); } class AttachedHardwareProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid); ~AttachedHardwareProbe() override; private: AttachedHardwareProbe(const Probe &probe, std::vector links); std::vector links_; }; AttachedHardwareProbe::AttachedHardwareProbe( const Probe &probe, std::vector links) : AttachedProbe(probe), links_(std::move(links)) { } AttachedHardwareProbe::~AttachedHardwareProbe() { for (struct bpf_link *link : links_) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for hardware probe: " << strerror(errno); } } Result> AttachedHardwareProbe::make( Probe &probe, const BpfProgram &prog, std::optional pid) { 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 (const 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; bool has_error = false; std::vector links; std::vector cpus = util::get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = open_perf_event(PERF_TYPE_HARDWARE, type, period, 0, pid.has_value() ? *pid : -1, cpu, group_fd); if (perf_event_fd < 0) { has_error = true; break; } auto *link = bpf_program__attach_perf_event(prog.bpf_prog(), perf_event_fd); if (!link) { has_error = true; break; } links.push_back(link); } if (has_error) { for (struct bpf_link *link : links) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for hardware probe: " << strerror(errno); } return make_error(); } return std::unique_ptr( new AttachedHardwareProbe(probe, links)); } class AttachedFentryProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog); ~AttachedFentryProbe() override; private: AttachedFentryProbe(const Probe &probe, int tracing_fd); int tracing_fd_; }; AttachedFentryProbe::AttachedFentryProbe(const Probe &probe, int tracing_fd) : AttachedProbe(probe), tracing_fd_(tracing_fd) { } AttachedFentryProbe::~AttachedFentryProbe() { close(tracing_fd_); } Result> AttachedFentryProbe::make( Probe &probe, const BpfProgram &prog) { int tracing_fd = bpf_raw_tracepoint_open(nullptr, prog.fd()); if (tracing_fd < 0) { return make_error(); } return std::unique_ptr( new AttachedFentryProbe(probe, tracing_fd)); } class AttachedRawtracepointProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog); ~AttachedRawtracepointProbe() override; private: AttachedRawtracepointProbe(const Probe &probe, int tracing_fd); int tracing_fd_; }; AttachedRawtracepointProbe::AttachedRawtracepointProbe(const Probe &probe, int tracing_fd) : AttachedProbe(probe), tracing_fd_(tracing_fd) { } AttachedRawtracepointProbe::~AttachedRawtracepointProbe() { close(tracing_fd_); } Result> AttachedRawtracepointProbe:: make(Probe &probe, const BpfProgram &prog) { int tracing_fd = bpf_raw_tracepoint_open(nullptr, prog.fd()); if (tracing_fd < 0) { if (tracing_fd == -ENOENT) { return make_error("Probe does not exist: " + probe.name); } else if (tracing_fd == -EINVAL) { return make_error( "Maybe trying to access arguments beyond what's available in " "this tracepoint"); } return make_error(); } return std::unique_ptr( new AttachedRawtracepointProbe(probe, tracing_fd)); } class AttachedIterProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid); ~AttachedIterProbe() override; int link_fd() override; private: AttachedIterProbe(const Probe &probe, int iter_link_fd); const int iter_link_fd_; }; AttachedIterProbe::AttachedIterProbe(const Probe &probe, int iter_link_fd) : AttachedProbe(probe), iter_link_fd_(iter_link_fd) { } AttachedIterProbe::~AttachedIterProbe() { close(iter_link_fd_); } Result> AttachedIterProbe::make( Probe &probe, const BpfProgram &prog, std::optional pid) { int iter_fd = -1; if (!pid.has_value()) { iter_fd = bpf_link_create(prog.fd(), 0, static_cast( libbpf::BPF_TRACE_ITER), nullptr); } else { BPFTRACE_LIBBPF_OPTS(bpf_link_create_opts, opts); union bpf_iter_link_info linfo; memset(&linfo, 0, sizeof(linfo)); linfo.task.pid = *pid; opts.iter_info = &linfo; opts.iter_info_len = sizeof(linfo); iter_fd = bpf_link_create(prog.fd(), 0, static_cast( libbpf::BPF_TRACE_ITER), &opts); } if (iter_fd < 0) { return make_error(); } return std::unique_ptr( new AttachedIterProbe(probe, iter_fd)); } int AttachedIterProbe::link_fd() { return iter_link_fd_; } class AttachedWatchpointProbe : public AttachedProbe { public: static Result> make( Probe &probe, const BpfProgram &prog, std::optional pid, const std::string &mode); ~AttachedWatchpointProbe() override; private: AttachedWatchpointProbe(const Probe &probe, std::vector links); std::vector links_; }; AttachedWatchpointProbe::AttachedWatchpointProbe( const Probe &probe, std::vector links) : AttachedProbe(probe), links_(std::move(links)) { } AttachedWatchpointProbe::~AttachedWatchpointProbe() { for (struct bpf_link *link : links_) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for watchpoint probe: " << strerror(errno); } } Result> AttachedWatchpointProbe::make( Probe &probe, const BpfProgram &prog, std::optional 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; // https://man7.org/linux/man-pages/man2/perf_event_open.2.html attr.bp_len = (attr.bp_type & HW_BREAKPOINT_X) ? sizeof(long) : probe.len; // Generate a notification every 1 event; we care about every event attr.sample_period = 1; // Attach to threads. // // NB: this only works for threads created after attachment // (limitation of perf_event_open)! attr.inherit = 1; std::vector cpus; if (pid.has_value()) { cpus = { -1 }; } else { cpus = util::get_online_cpus(); } std::string err_msg; bool has_error = false; std::vector links; 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.has_value() ? *pid : -1, cpu, -1, PERF_FLAG_FD_CLOEXEC); if (perf_event_fd < 0) { if (errno == ENOSPC) err_msg = "No more HW registers left"; has_error = true; break; } auto *link = bpf_program__attach_perf_event(prog.bpf_prog(), perf_event_fd); if (!link) { has_error = true; break; } links.push_back(link); } if (has_error) { for (struct bpf_link *link : links) { if (bpf_link__destroy(link)) LOG(WARNING) << "failed to destroy link for watchpoint probe: " << strerror(errno); } return make_error(std::move(err_msg)); } return std::unique_ptr( new AttachedWatchpointProbe(probe, links)); } AttachedProbe::AttachedProbe(const Probe &probe) : probe_(probe) { } Result> AttachedProbe::make( Probe &probe, const BpfProgram &prog, std::optional pid, BPFtrace &bpftrace, bool safe_mode) { LOG(V1) << "Trying to attach probe: " << probe.name; switch (probe.type) { case ProbeType::kprobe: case ProbeType::kretprobe: { if (!probe.funcs.empty()) { return AttachedMultiKprobeProbe::make(probe, prog); } return AttachedKprobeProbe::make(probe, prog, bpftrace); } case ProbeType::tracepoint: { return AttachedTracepointProbe::make(probe, prog); } case ProbeType::profile: { return AttachedProfileProbe::make(probe, prog, pid); } case ProbeType::interval: { return AttachedIntervalProbe::make(probe, prog); } case ProbeType::software: { return AttachedSoftwareProbe::make(probe, prog, pid); } case ProbeType::hardware: { return AttachedHardwareProbe::make(probe, prog, pid); } case ProbeType::fentry: case ProbeType::fexit: { return AttachedFentryProbe::make(probe, prog); } case ProbeType::iter: { return AttachedIterProbe::make(probe, prog, pid); } case ProbeType::rawtracepoint: { return AttachedRawtracepointProbe::make(probe, prog); } case ProbeType::usdt: { return AttachedUSDTProbe::make(probe, prog, pid); } case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: { return AttachedWatchpointProbe::make(probe, prog, pid, probe.mode); } case ProbeType::uprobe: case ProbeType::uretprobe: { if (!probe.funcs.empty()) { return AttachedMultiUprobeProbe::make(probe, prog, pid); } return AttachedUprobeProbe::make(probe, prog, pid, safe_mode); } case ProbeType::invalid: case ProbeType::special: case ProbeType::benchmark: { LOG(BUG) << "invalid attached probe type \"" << probe.type << "\""; } } return make_error(); } } // namespace bpftrace bpftrace-0.24.1/src/attached_probe.h000066400000000000000000000027551506776124200173130ustar00rootroot00000000000000#pragma once #include #include #include #include #include "bpffeature.h" #include "bpfprogram.h" #include "btf.h" #include "probe_types.h" #include "usdt.h" #include "util/result.h" 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 AttachError : public ErrorInfo { public: AttachError(std::string &&msg) : msg_(std::move(msg)) {}; AttachError() = default; static char ID; void log(llvm::raw_ostream &OS) const override; const std::string &msg() const { return msg_; } private: std::string msg_; }; class AttachedProbe { public: static Result> make(Probe &probe, const BpfProgram &prog, std::optional pid, BPFtrace &bpftrace, bool safe_mode = true); virtual ~AttachedProbe() = default; AttachedProbe(const AttachedProbe &) = delete; AttachedProbe &operator=(const AttachedProbe &) = delete; virtual int link_fd() { return -1; } virtual size_t probe_count() const { return 1; } const Probe &probe() const { return probe_; } protected: AttachedProbe(const Probe &probe); const Probe &probe_; }; } // namespace bpftrace bpftrace-0.24.1/src/benchmark.cpp000066400000000000000000000106361506776124200166310ustar00rootroot00000000000000#include #include #include #include #include #include "ast/ast.h" #include "ast/context.h" #include "benchmark.h" #include "util/time.h" namespace bpftrace { char TimerError::ID; void TimerError::log(llvm::raw_ostream &OS) const { OS << "timer error: " << strerror(err_); } using time_point = std::chrono::time_point; static Result processor_time() { struct timespec ts = {}; int rc = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); if (rc < 0) { return make_error(errno); } return time_point(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)); } static int64_t delta(time_point start, time_point end) { return std::chrono::duration_cast(end - start) .count(); } Result<> benchmark(std::ostream &out, ast::PassManager &mgr) { ast::PassContext ctx; // See below; we aggregate at the end. int64_t full_mean = 0; double full_variance = 0; size_t full_count = 0; // We print out the confidence interval at p95, which corresponds to a // z-score of 1.96 (see the `err` value below). auto emit = [&](const std::string &name, int64_t total, int64_t count, double variance) { size_t mean = total / count; auto stddev = std::sqrt(variance); auto err = static_cast(1.96 * stddev / std::sqrt(static_cast(count))); auto [unit, scale] = util::duration_str(std::chrono::nanoseconds(mean)); out << std::left << std::setw(30) << name; out << std::left << std::setw(8) << count; out << std::left << std::setw(14) << total; out << mean / scale << " ± " << err / scale << " " << unit << std::endl; }; auto ok = mgr.foreach([&](auto &pass) -> Result<> { // Copy out the AST. We allow passes to mutate the AST, and therefore we // copy this out and reset it each time. ast::ASTContext saved; if (ctx.has()) { auto &ast = ctx.get(); saved.root = saved.clone_node(ast.root, ast::Location()); } // We run the function until we are able to accumulate at least three // iterations, and 100 milliseconds (but we never bother doing more than // 10,000). This should provide reasonable data for the below. The times // are all recorded in process CPU time, only while the pass itself is // running. We may accumulate additional time rebuilding the AST, etc. int64_t goal = std::chrono::duration_cast( std::chrono::milliseconds(100)) .count(); std::vector samples; int64_t total = 0; while (true) { auto start = processor_time(); if (!start) { return start.takeError(); } auto ok = pass.run(ctx); if (!ok) { return ok.takeError(); } auto end = processor_time(); if (!end) { return end.takeError(); } int64_t current = delta(*start, *end); samples.push_back(current); total += current; // Do we have enough (or too much)? if (samples.size() >= 10000 || (samples.size() > 3 && total >= goal)) { break; } // Restore the original tree. auto &ast = ctx.get(); ast.clear(); ast.root = clone(ast, saved.root, ast::Location()); } // Compute the variance of the samples. int64_t mean = total / samples.size(); double variance = 0; for (const auto &sample : samples) { variance += std::pow(static_cast(sample - mean), 2); } emit(pass.name(), total, samples.size(), variance); // Aggregate for printing the final stats. Note that we treat each pass as // independent, therefore the final variance is the sum of the variances. full_mean += mean; full_variance += variance; full_count++; return OK(); }); if (!ok) { out << "FAIL\n"; // See below. return ok.takeError(); } // The final `PASS` is emitted when all passes have finished correctly. This // makes the output format compatible with `gobench` or other aggregation // tools that can compare benchmarks. emit("total", full_mean * full_count, full_count, full_variance); out << "PASS\n"; return OK(); } } // namespace bpftrace bpftrace-0.24.1/src/benchmark.h000066400000000000000000000006101506776124200162650ustar00rootroot00000000000000#pragma once #include #include "ast/pass_manager.h" #include "util/result.h" namespace bpftrace { class TimerError : public ErrorInfo { public: TimerError(int err) : err_(err) {}; static char ID; void log(llvm::raw_ostream &OS) const override; private: int err_; }; Result benchmark(std::ostream &out, ast::PassManager &mgr); } // namespace bpftrace bpftrace-0.24.1/src/bfd-disasm.cpp000066400000000000000000000057671506776124200167210ustar00rootroot00000000000000#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 #include #include #include "bfd-disasm.h" #include "util/system.h" namespace bpftrace { BfdDisasm::BfdDisasm(std::string &path) { 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 (!ec) { 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; auto tpath = util::get_pid_exe("self"); if (!tpath) { return AlignState::Fail; } 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.24.1/src/bfd-disasm.h000066400000000000000000000004641506776124200163530ustar00rootroot00000000000000#pragma once #include "disasm.h" namespace bpftrace { class BfdDisasm : public IDisasm { public: explicit BfdDisasm(std::string &path); ~BfdDisasm() override; AlignState is_aligned(uint64_t offset, uint64_t pc) override; private: int fd_ = -1; uint64_t size_{ 0 }; }; } // namespace bpftrace bpftrace-0.24.1/src/bpf_assembler.h000066400000000000000000000103011506776124200171350ustar00rootroot00000000000000// 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.24.1/src/bpfbytecode.cpp000066400000000000000000000310541506776124200171620ustar00rootroot00000000000000#include "bpfbytecode.h" #include #include #include #include "ast/passes/named_param.h" #include "bpftrace.h" #include "globalvars.h" #include "log.h" #include "util/bpf_names.h" #include "util/exceptions.h" #include "util/wildcard.h" #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 (std::string_view::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.contains(DebugStage::Verifier)) 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: " << std::strerror(errno); 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(util::get_function_name_for_probe( probe.name, probe.index, usdt_location_idx)); if (prog == programs_.end()) { prog = programs_.find(util::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 << " (expanded 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::GlobalVarMap &&global_var_vals) { bpftrace.resources.global_vars.update_global_vars( bpf_object_.get(), section_names_to_global_vars_map_, std::move(global_var_vals), bpftrace.ncpus_, bpftrace.max_cpu_id_); } uint64_t BpfBytecode::get_event_loss_counter(BPFtrace &bpftrace, int max_cpu_id) { auto *current_values = bpftrace.resources.global_vars.get_global_var( bpf_object_.get(), globalvars::EVENT_LOSS_COUNTER_SECTION_NAME, section_names_to_global_vars_map_); uint64_t current_value = 0; for (int i = 0; i < max_cpu_id; ++i) { current_value += *current_values; current_values++; } return current_value; } 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 == std::string_view::npos) return; std::string_view call_pattern = " call "; auto call_pos = log.rfind(call_pattern, err_pos); if (call_pos == std::string_view::npos) return; auto helper_begin = call_pos + call_pattern.size(); auto hash_pos = log.find("#", helper_begin); if (hash_pos == std::string_view::npos) return; auto eol = log.find("\n", hash_pos + 1); if (eol == std::string_view::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 !util::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.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.benchmark_probes, btf, feature, config); prepare_progs(resources.signal_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.contains(DebugStage::Verifier)) { 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, "program of this type cannot use helper", " 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"); auto err_pos = log.find("from non-GPL compatible program"); if (err_pos != std::string_view::npos) { LOG(ERROR) << "Your bpftrace program cannot load because you are using " "a license that is non-GPL compatible. License: " << config.license; LOG(HINT) << "Read more about BPF programs and licensing: " "https://docs.kernel.org/bpf/" "bpf_licensing.html#using-bpf-programs-in-the-linux-kernel"; } 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 util::FatalUserException("Loading BPF object(s) failed."); } void BpfBytecode::prepare_progs(const std::vector &probes, const BTF &btf, BPFfeature &feature, const Config &config) { for (const 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(); } } void BpfBytecode::attach_external() { for (const auto &prog : programs_) { auto *p = prog.second.bpf_prog(); if (bpf_program__autoattach(p)) { bpf_program__attach(p); } } } bool BpfBytecode::all_progs_loaded() { return std::ranges::all_of(programs_, [](const auto &prog) { return prog.second.fd() >= 0; }); } bool BpfBytecode::hasMap(MapType internal_type) const { return maps_.contains(to_string(internal_type)); } bool BpfBytecode::hasMap(const StackType &stack_type) const { return maps_.contains(stack_type.name()); } 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 (const 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.24.1/src/bpfbytecode.h000066400000000000000000000056041506776124200166310ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "bpffeature.h" #include "bpfmap.h" #include "bpfprogram.h" #include "config.h" #include "globalvars.h" #include "probe_types.h" #include "required_resources.h" #include "util/result.h" 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 ast::State<"bytecode"> { public: BpfBytecode() = default; 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, globalvars::GlobalVarMap &&global_var_vals); uint64_t get_event_loss_counter(BPFtrace &bpftrace, int max_cpu_id); void load_progs(const RequiredResources &resources, const BTF &btf, BPFfeature &feature, const Config &config); void attach_external(); 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_; }; 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.24.1/src/bpffeature.cpp000066400000000000000000000466651506776124200170350ustar00rootroot00000000000000#include "bpffeature.h" #include #include #include #include #include #include #include #include #include #include #include #include "bpf_assembler.h" #include "btf.h" #include "dwarf_parser.h" #include "tracefs/tracefs.h" #include "util/kernel.h" #include "util/strings.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) namespace bpftrace { using util::KernelVersionMethod; int BPFnofeature::parse(const char* str) { for (auto feat : util::split_string(str, ',')) { // Remember to update bpftrace.adoc! if (feat == "kprobe_multi") { kprobe_multi_ = true; } else if (feat == "kprobe_session") { kprobe_session_ = 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[] = { KernelVersionMethod::vDSO, KernelVersionMethod::UTS, KernelVersionMethod::File }; for (KernelVersionMethod method : methods) { auto version = kernel_version(method); if (method != KernelVersionMethod::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 try_create_link(libbpf::bpf_prog_type prog_type, const std::string_view prog_name, libbpf::bpf_attach_type expected_attach_type, const bpf_link_create_opts& link_opts, std::optional expected_err) { bool result = false; BPFTRACE_LIBBPF_OPTS( bpf_prog_load_opts, load_opts, .expected_attach_type = static_cast( expected_attach_type)); struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }; int progfd = bpf_prog_load(static_cast<::bpf_prog_type>(prog_type), prog_name.data(), "GPL", reinterpret_cast(insns), ARRAY_SIZE(insns), &load_opts); if (progfd < 0) return false; int linkfd = bpf_link_create(progfd, 0, static_cast( expected_attach_type), &link_opts); result = expected_err.has_value() ? linkfd < 0 && -errno == *expected_err : linkfd >= 0; if (linkfd >= 0) { close(linkfd); } close(progfd); return result; } 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); link_opts.kprobe_multi.syms = &sym; link_opts.kprobe_multi.cnt = 1; has_kprobe_multi_ = try_create_link(libbpf::BPF_PROG_TYPE_KPROBE, sym, libbpf::BPF_TRACE_KPROBE_MULTI, link_opts, std::nullopt); return *has_kprobe_multi_; } bool BPFfeature::has_kprobe_session() { if (has_kprobe_session_.has_value()) return *has_kprobe_session_; if (no_feature_.kprobe_session_) { has_kprobe_session_ = false; return *has_kprobe_session_; } const char* sym = "ksys_read"; BPFTRACE_LIBBPF_OPTS(bpf_link_create_opts, link_opts); link_opts.kprobe_multi.syms = &sym; link_opts.kprobe_multi.cnt = 1; has_kprobe_session_ = try_create_link(libbpf::BPF_PROG_TYPE_KPROBE, sym, libbpf::BPF_TRACE_KPROBE_SESSION, link_opts, std::nullopt); return *has_kprobe_session_; } bool BPFfeature::has_uprobe_multi() { if (has_uprobe_multi_.has_value()) return *has_uprobe_multi_; #ifdef HAVE_LIBBPF_UPROBE_MULTI if (no_feature_.uprobe_multi_) { has_uprobe_multi_ = false; return *has_uprobe_multi_; } 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; has_uprobe_multi_ = try_create_link(libbpf::BPF_PROG_TYPE_KPROBE, "uprobe_multi", libbpf::BPF_TRACE_UPROBE_MULTI, link_opts, -EBADF); #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_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()) }, { "send_signal", to_str(has_helper_send_signal()) }, { "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(btf_.has_module_btf()) }, { "map batch", to_str(has_map_batch()) }, }; 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()) }, { "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()) }, { "kprobe_session", to_str(has_kprobe_session()) }, { "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_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_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.24.1/src/bpffeature.h000066400000000000000000000162401506776124200164640ustar00rootroot00000000000000#pragma once #include "btf.h" #include "kfuncs.h" #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() = default; int parse(const char* str); protected: bool kprobe_multi_{ false }; bool kprobe_session_{ false }; bool uprobe_multi_{ false }; friend class BPFfeature; }; class BPFfeature { public: BPFfeature(BPFnofeature& no_feature, BTF& btf) : no_feature_(no_feature), btf_(btf) { } 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_kprobe_multi(); bool has_kprobe_session(); bool has_uprobe_multi(); bool has_skb_output(); bool has_prog_fentry(); // These are virtual so they can be overridden in tests by the mock virtual bool has_fentry(); virtual bool has_kernel_func(Kfunc kfunc); virtual bool has_iter(std::string name); std::string report(); 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(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_HELPER_TEST(loop, libbpf::BPF_PROG_TYPE_KPROBE); // Added in 5.13. 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_kprobe_multi_; std::optional has_kprobe_session_; std::optional has_uprobe_multi_; std::optional has_skb_output_; std::optional has_prog_fentry_; 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); BPFnofeature no_feature_; BTF& btf_; }; #undef DEFINE_PROG_TEST #undef DEFINE_MAP_TEST #undef DEFINE_HELPER_TEST } // namespace bpftrace bpftrace-0.24.1/src/bpfmap.cpp000066400000000000000000000211141506776124200161350ustar00rootroot00000000000000#include #include #include "bpfmap.h" #include "util/stats.h" #include "util/tseries.h" namespace bpftrace { char BpfMapError::ID = 0; const std::unordered_map BPF_MAP_TYPES = { { "hash", libbpf::BPF_MAP_TYPE_HASH }, { "lruhash", libbpf::BPF_MAP_TYPE_LRU_HASH }, { "percpuhash", libbpf::BPF_MAP_TYPE_PERCPU_HASH }, { "percpuarray", libbpf::BPF_MAP_TYPE_PERCPU_ARRAY }, { "percpulruhash", libbpf::BPF_MAP_TYPE_LRU_PERCPU_HASH } }; int BpfMap::fd() const { return bpf_map__fd(bpf_map_); } libbpf::bpf_map_type BpfMap::type() const { return type_; } const std::string &BpfMap::bpf_name() const { return name_; } std::string BpfMap::name() const { return bpftrace_map_name(bpf_name()); } uint32_t BpfMap::max_entries() const { return max_entries_; } bool BpfMap::is_stack_map() const { return name().starts_with("stack_"); } bool BpfMap::is_per_cpu_type() const { return type() == libbpf::BPF_MAP_TYPE_PERCPU_HASH || type() == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY || type() == libbpf::BPF_MAP_TYPE_LRU_PERCPU_HASH; } bool BpfMap::is_printable() const { // Internal maps are not printable return bpf_name().starts_with("AT_"); } std::vector BpfMap::collect_keys() const { const void *last_key = nullptr; std::vector keys; while (true) { int rc = 0; auto key = OpaqueValue::alloc(key_size_, [&](void *data) { rc = bpf_map_get_next_key(fd(), last_key, data); }); if (rc != 0) { break; } last_key = keys.emplace_back(std::move(key)).data(); } return keys; } Result<> BpfMap::zero_out(int nvalues) const { auto keys = collect_keys(); auto value_size = static_cast(value_size_) * static_cast(nvalues); auto zero = OpaqueValue::alloc(value_size, [&](void *data) { memset(data, 0, value_size); }); for (auto &k : keys) { int err = bpf_map_update_elem(fd(), k.data(), zero.data(), BPF_EXIST); if (err && err != -ENOENT) { return make_error(name_, "zero", err); } } return OK(); } Result<> BpfMap::clear(int nvalues) const { if (!is_bpf_map_clearable(type())) { return zero_out(nvalues); } auto keys = collect_keys(); for (auto &k : keys) { int err = bpf_map_delete_elem(fd(), k.data()); if (err && err != -ENOENT) { return make_error(name_, "clear", err); } } return OK(); } Result<> BpfMap::update_elem(const void *key, const void *value) const { auto err = bpf_map_update_elem(fd(), key, value, BPF_ANY); if (err != 0) { return make_error(name_, "update", err); } return OK(); } Result<> BpfMap::lookup_elem(const void *key, void *value) const { auto err = bpf_map_lookup_elem(fd(), key, value); if (err != 0) { return make_error(name_, "lookup", err); } return OK(); } Result BpfMap::collect_elements(int nvalues) const { auto keys = collect_keys(); MapElements values_by_key; for (auto &key : keys) { int err = 0; auto value = OpaqueValue::alloc( static_cast(value_size_) * static_cast(nvalues), [&](void *data) { err = bpf_map_lookup_elem(fd(), key.data(), data); }); if (err == -ENOENT) { // key was removed by the eBPF program during bpf_map_get_next_key() and // bpf_map_lookup_elem(), let's skip this key. continue; } else if (err) { return make_error(name_, "lookup", err); } values_by_key.emplace_back(std::move(key), std::move(value)); } return values_by_key; } Result BpfMap::collect_histogram_data(const MapInfo &map_info, int nvalues) const { auto keys = collect_keys(); HistogramMap values_by_key; for (auto &key : keys) { int err = 0; auto value = OpaqueValue::alloc( static_cast(value_size_) * static_cast(nvalues), [&](void *data) { err = bpf_map_lookup_elem(fd(), key.data(), data); }); if (err == -ENOENT) { // key was removed by the eBPF program during bpf_map_get_next_key() and // bpf_map_lookup_elem(), let's skip this key continue; } else if (err) { return make_error(name_, "lookup", err); } auto prefix = key.slice(0, map_info.key_type.GetSize()); auto bucket = key.slice(map_info.key_type.GetSize(), sizeof(uint64_t)); if (!values_by_key.contains(prefix)) { // New key - create a list of buckets for it if (map_info.value_type.IsHistTy()) values_by_key[prefix].resize(65 * 32, 0); else values_by_key[prefix].resize(1002, 0); } auto idx = bucket.bitcast(); values_by_key[prefix].at(idx) = util::reduce_value(value); } return values_by_key; } Result BpfMap::collect_tseries_data(const MapInfo &map_info, int nvalues) const { auto keys = collect_keys(); TSeriesMap values_by_key; const auto &tseries_args = std::get(map_info.detail); for (auto &key : keys) { int err = 0; auto value = OpaqueValue::alloc( static_cast(value_size_) * static_cast(nvalues), [&](void *data) { err = bpf_map_lookup_elem(fd(), key.data(), data); }); if (err == -ENOENT) { // key was removed by the eBPF program during bpf_map_get_next_key() and // bpf_map_lookup_elem(), let's skip this key continue; } else if (err) { return make_error(name_, "lookup", err); } auto prefix = key.slice(0, map_info.key_type.GetSize()); auto bucket = key.slice(map_info.key_type.GetSize(), sizeof(uint64_t)); auto tseries = values_by_key.try_emplace(prefix).first; auto [epoch, v] = util::reduce_tseries_value(value, tseries_args.value_type, tseries_args.agg); tseries->second.emplace(epoch, std::move(v)); } return values_by_key; } 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 } libbpf::bpf_map_type get_bpf_map_type(const SizedType &val_type, bool scalar) { if (val_type.IsCountTy() && scalar) { 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; } } std::optional get_bpf_map_type(const std::string &name) { auto found = BPF_MAP_TYPES.find(name); if (found == BPF_MAP_TYPES.end()) { return std::nullopt; } return found->second; } std::string get_bpf_map_type_str(libbpf::bpf_map_type map_type) { for (const auto &pair : BPF_MAP_TYPES) { if (pair.second == map_type) { return pair.first; } } return "unknown"; } void add_bpf_map_types_hint(std::stringstream &hint) { hint << "Valid map types: "; int num_types = BPF_MAP_TYPES.size(); for (const auto &bpf_type : BPF_MAP_TYPES) { hint << bpf_type.first; num_types--; if (num_types != 0) { hint << ", "; } } } bool is_array_map(const SizedType &val_type, bool scalar) { auto map_type = get_bpf_map_type(val_type, scalar); return map_type == libbpf::BPF_MAP_TYPE_ARRAY || map_type == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY; } bool bpf_map_types_compatible(const SizedType &val_type, bool scalar, libbpf::bpf_map_type kind) { auto kind_from_stype = get_bpf_map_type(val_type, scalar); if (kind_from_stype == kind) { return true; } if ((kind_from_stype == libbpf::BPF_MAP_TYPE_HASH || kind_from_stype == libbpf::BPF_MAP_TYPE_LRU_HASH) && (kind == libbpf::BPF_MAP_TYPE_HASH || kind == libbpf::BPF_MAP_TYPE_LRU_HASH)) { return true; } if ((kind_from_stype == libbpf::BPF_MAP_TYPE_PERCPU_HASH || kind_from_stype == libbpf::BPF_MAP_TYPE_LRU_PERCPU_HASH) && (kind == libbpf::BPF_MAP_TYPE_PERCPU_HASH || kind == libbpf::BPF_MAP_TYPE_LRU_PERCPU_HASH)) { return true; } // This doesn't work the opposite way if (kind == libbpf::BPF_MAP_TYPE_PERCPU_HASH && kind_from_stype == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY) { return true; } return false; } } // namespace bpftrace bpftrace-0.24.1/src/bpfmap.h000066400000000000000000000077421506776124200156150ustar00rootroot00000000000000#pragma once #include #include #include #include #include "map_info.h" #include "util/opaque.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace { class BpfMapError : public ErrorInfo { public: static char ID; std::string name_; std::string op_; int errno_; BpfMapError(std::string name_, std::string op_, int errno_) : name_(std::move(name_)), op_(std::move(op_)), errno_(errno_) { } void log(llvm::raw_ostream &OS) const override { OS << "BPF map operation " << op_ << " failed: " << std::strerror(-errno_) << " [map = " << name_ << "]"; } }; using util::OpaqueValue; using MapElements = std::vector>; using HistogramMap = std::map>; using TSeries = std::map; using TSeriesMap = std::map; 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, std::string name, uint32_t key_size, uint32_t value_size, uint32_t max_entries) : type_(type), name_(std::move(name)), key_size_(key_size), value_size_(value_size), max_entries_(max_entries) { } virtual ~BpfMap() = default; int fd() const; libbpf::bpf_map_type type() const; const std::string &bpf_name() const; std::string name() const; uint32_t max_entries() const; bool is_stack_map() const; bool is_per_cpu_type() const; bool is_printable() const; std::vector collect_keys() const; virtual Result collect_elements(int nvalues) const; virtual Result collect_histogram_data(const MapInfo &map_info, int nvalues) const; virtual Result collect_tseries_data(const MapInfo &map_info, int nvalues) const; Result<> zero_out(int nvalues) const; Result<> clear(int nvalues) const; Result<> update_elem(const void *key, const void *value) const; Result<> lookup_elem(const void *key, void *value) const; private: struct bpf_map *bpf_map_; libbpf::bpf_map_type type_; std::string 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.starts_with("AT_")) 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; } libbpf::bpf_map_type get_bpf_map_type(const SizedType &val_type, bool scalar); std::optional get_bpf_map_type(const std::string &name); std::string get_bpf_map_type_str(libbpf::bpf_map_type map_type); void add_bpf_map_types_hint(std::stringstream &hint); bool is_array_map(const SizedType &val_type, bool scalar); bool bpf_map_types_compatible(const SizedType &val_type, bool scalar, libbpf::bpf_map_type kind); } // namespace bpftrace bpftrace-0.24.1/src/bpfprogram.cpp000066400000000000000000000074501506776124200170360ustar00rootroot00000000000000#include #include #include #include #include "attached_probe.h" #include "bpfprogram.h" #include "log.h" #include "util/exceptions.h" #include "util/fd.h" 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) { auto 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; else if (probe.type == ProbeType::rawtracepoint) attach_type = libbpf::BPF_TRACE_RAW_TP; // 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) && !probe.funcs.empty() && probe.path.empty()) { if (probe.is_session && feature.has_kprobe_session()) attach_type = libbpf::BPF_TRACE_KPROBE_SESSION; else if (feature.has_kprobe_multi()) 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 && probe.type != ProbeType::rawtracepoint) return; const std::string &mod = probe.path; const std::string &fun = probe.attach_point; std::string attach_target = !mod.empty() ? mod + ":" + fun : fun; std::string btf_fun; __u32 btf_kind = BTF_KIND_FUNC; if (probe.type == ProbeType::iter) { btf_fun = "bpf_iter_" + fun; } else if (probe.type == ProbeType::rawtracepoint) { btf_fun = "btf_trace_" + fun; btf_kind = BTF_KIND_TYPEDEF; } else { btf_fun = fun; } std::string err_msg; if ((probe.type == ProbeType::fentry || probe.type == ProbeType::fexit) && mod == "bpf") { int raw_fd = bpf_prog_get_fd_by_id(static_cast<__u32>(probe.bpf_prog_id)); if (raw_fd < 0) { err_msg = "No valid BPF program found with name: " + fun + " and id: " + std::to_string(probe.bpf_prog_id) + "."; } else { bpf_prog_fd_ = util::FD(raw_fd); attach_target = fun; } } else if (btf.get_btf_id(btf_fun, mod, btf_kind) < 0) { err_msg = "No BTF found for " + attach_target + "."; } if (!err_msg.empty()) { if (config.missing_probes == ConfigMissingProbes::error) { LOG(ERROR) << err_msg; } else if (config.missing_probes == ConfigMissingProbes::warn) { LOG(WARNING) << err_msg; } bpf_program__set_autoload(bpf_prog_, false); } bpf_program__set_attach_target(bpf_prog_, bpf_prog_fd_ ? *bpf_prog_fd_ : 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.24.1/src/bpfprogram.h000066400000000000000000000020051506776124200164720ustar00rootroot00000000000000#pragma once #include #include "bpffeature.h" #include "btf.h" #include "config.h" #include "probe_types.h" #include "util/fd.h" 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_; std::optional bpf_prog_fd_ = std::nullopt; }; } // namespace bpftrace bpftrace-0.24.1/src/bpftrace.cpp000066400000000000000000001211501506776124200164570ustar00rootroot00000000000000#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 "ast/context.h" #include "async_action.h" #include "attached_probe.h" #include "bpfmap.h" #include "bpfprogram.h" #include "bpftrace.h" #include "btf.h" #include "log.h" #include "scopeguard.h" #include "util/bpf_names.h" #include "util/cgroup.h" #include "util/cpus.h" #include "util/exceptions.h" #include "util/kernel.h" #include "util/paths.h" #include "util/strings.h" #include "util/system.h" #include "util/wildcard.h" using namespace std::chrono_literals; 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; static void log_probe_attach_failure(const std::string &err_msg, const std::string &name, ConfigMissingProbes missing_probes) { if (missing_probes == ConfigMissingProbes::error) { if (!err_msg.empty()) { LOG(ERROR) << err_msg; } LOG(ERROR) << "Unable to attach probe: " << name << ". If this is expected, set the 'missing_probes' " "config variable to 'warn'."; } else if (missing_probes == ConfigMissingProbes::warn) { if (!err_msg.empty()) { LOG(WARNING) << err_msg; } LOG(WARNING) << "Unable to attach probe: " << name << ". Skipping."; } } BPFtrace::~BPFtrace() { close_pcaps(); } Probe BPFtrace::generateWatchpointSetupProbe(const ast::AttachPoint &ap, const ast::Probe &probe) { Probe setup_probe; setup_probe.name = util::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 = util::get_watchpoint_setup_probe_name( probe.orig_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, ast::ExpansionType expansion, std::set expanded_funcs, int usdt_location_idx) { Probe probe; probe.path = ap.target; probe.attach_point = ap.func; probe.type = probetype(ap.provider); probe.log_size = config_->log_size; probe.orig_name = p.orig_name; probe.ns = ap.ns; probe.name = ap.name(); 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; probe.is_session = expansion == ast::ExpansionType::SESSION; probe.funcs = std::move(expanded_funcs); probe.bpf_prog_id = ap.bpf_prog_id; return probe; } int BPFtrace::add_probe(const ast::AttachPoint &ap, const ast::Probe &p, ast::ExpansionType expansion, std::set expanded_funcs, int usdt_location_idx) { auto type = probetype(ap.provider); auto probe = generate_probe( ap, p, expansion, std::move(expanded_funcs), usdt_location_idx); // Add the new probe(s) to resources if (ap.provider == "begin" || ap.provider == "end") { // special probes auto target = ap.target.empty() ? "" : "_" + ap.target; auto name = ap.provider + target; resources.special_probes[name] = std::move(probe); } else if (ap.provider == "bench") { resources.benchmark_probes.emplace_back(std::move(probe)); } else if (ap.provider == "self") { if (ap.target == "signal") { resources.signal_probes.emplace_back(std::move(probe)); } } else if ((type == ProbeType::watchpoint || type == ProbeType::asyncwatchpoint) && !ap.func.empty()) { // (async)watchpoint - generate also the setup probe resources.probes.emplace_back(generateWatchpointSetupProbe(ap, p)); resources.watchpoint_probes.emplace_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.contains(&p) && util::is_exe(ap.target)) { usyms_.cache(ap.target, this->pid()); } return 0; } int BPFtrace::num_probes() const { return resources.special_probes.size() + resources.probes.size() + resources.signal_probes.size() + resources.benchmark_probes.size(); } void BPFtrace::request_finalize() { finalize_ = true; attached_probes_.clear(); if (child_) child_->terminate(); } // PerfEventContext is our callback wrapper. struct PerfEventContext { PerfEventContext(BPFtrace &b, async_action::AsyncHandlers &handlers, output::Output &o) : bpftrace(b), handlers(handlers), output(o) {}; BPFtrace &bpftrace; async_action::AsyncHandlers &handlers; output::Output &output; }; void perf_event_printer(void *cb_cookie, void *raw_data, int size) { auto *ctx = static_cast(cb_cookie); // N.B. This will copy the value into its own buffer, potentially allocating // and freeing a new chunk if it is larger than a single word. This is // guaranteed to be aligned. auto data = OpaqueValue::alloc(size, [&](char *data) { memcpy(data, raw_data, size); }); // Ignore the remaining events if perf_event_printer is called during // finalization stage (exit() builtin has been called) if (ctx->bpftrace.finalize_) return; if (bpftrace::BPFtrace::exitsig_recv) { ctx->bpftrace.request_finalize(); return; } // async actions auto printf_id = async_action::AsyncAction(data.bitcast()); if (printf_id == async_action::AsyncAction::exit) { ctx->handlers.exit(data); return; } else if (printf_id == async_action::AsyncAction::print) { ctx->handlers.print_map(data); return; } else if (printf_id == async_action::AsyncAction::print_non_map) { ctx->handlers.print_non_map(data); return; } else if (printf_id == async_action::AsyncAction::clear) { ctx->handlers.clear_map(data); return; } else if (printf_id == async_action::AsyncAction::zero) { ctx->handlers.zero_map(data); return; } else if (printf_id == async_action::AsyncAction::time) { ctx->handlers.time(data); return; } else if (printf_id == async_action::AsyncAction::join) { ctx->handlers.join(data); return; } else if (printf_id == async_action::AsyncAction::runtime_error) { ctx->handlers.runtime_error(data); return; } else if (printf_id == async_action::AsyncAction::watchpoint_attach) { ctx->handlers.watchpoint_attach(data); return; } else if (printf_id == async_action::AsyncAction::watchpoint_detach) { ctx->handlers.watchpoint_detach(data); return; } else if (printf_id == async_action::AsyncAction::skboutput) { ctx->handlers.skboutput(data); return; } else if (printf_id >= async_action::AsyncAction::syscall && printf_id <= async_action::AsyncAction::syscall_end) { ctx->handlers.syscall(data); return; } else if (printf_id >= async_action::AsyncAction::cat && printf_id <= async_action::AsyncAction::cat_end) { ctx->handlers.cat(data); return; } else if (printf_id >= async_action::AsyncAction::printf && printf_id <= async_action::AsyncAction::printf_end) { ctx->handlers.printf(data); return; } else { LOG(BUG) << "Unknown printf_id: " << static_cast(printf_id); } } int ringbuf_printer(void *cb_cookie, void *data, size_t size) { perf_event_printer(cb_cookie, data, size); return 0; } void BPFtrace::add_param(const std::string ¶m) { params_.emplace_back(param); } std::string BPFtrace::get_param(size_t i) const { if (params_.size() < i) { return ""; } 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 *ctx = static_cast(cb_cookie); ctx->output.lost_events(lost); } Result> BPFtrace::attach_probe( Probe &probe, const BpfBytecode &bytecode) { const auto &program = bytecode.getProgramForProbe(probe); std::optional pid = child_ ? std::make_optional(child_->pid()) : this->pid(); auto ap = AttachedProbe::make(probe, program, pid, *this, safe_mode_); if (!ap) { auto missing_probes = config_->missing_probes; auto ok = handleErrors(std::move(ap), [&](const AttachError &err) { log_probe_attach_failure(err.msg(), probe.name, missing_probes); }); return make_error(); } else { return std::move(*ap); } } bool attach_reverse(const Probe &p) { switch (p.type) { case ProbeType::special: case ProbeType::benchmark: case ProbeType::kprobe: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::iter: return true; case ProbeType::kretprobe: case ProbeType::tracepoint: case ProbeType::profile: case ProbeType::interval: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::hardware: case ProbeType::rawtracepoint: case ProbeType::software: 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->link_fd(); 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(); const auto max_probes = config_->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; } return 0; } int BPFtrace::run(output::Output &out, const ast::CDefinitions &c_definitions, BpfBytecode bytecode) { int err = prerun(); if (err) return err; bytecode_ = std::move(bytecode); bytecode_.set_map_ids(resources); try { bytecode_.load_progs(resources, *btf_, *feature_, *config_); } catch (const HelperVerifierError &e) { // To provide the most useful diagnostics, provide the error for every // callsite. After all, they all must be fixed. bool found = false; for (const auto &info : helper_use_loc_[e.func_id]) { bool first = true; for (const auto &loc : info.locations) { if (first) { LOG(ERROR, std::string(loc.source_location), std::vector(loc.source_context)) << e.what(); first = false; } else { LOG(ERROR, std::string(loc.source_location), std::vector(loc.source_context)) << "expanded from"; } } found = true; } if (!found) { // An error occurred, but we don't have location for this helper. It may // be a C file or elsewhere, and we need to plumb this through somehow. // At least inform the user what has gone wrong in this case. LOG(ERROR) << e.what(); } return -1; } catch (const std::runtime_error &e) { LOG(ERROR) << e.what(); return -1; } async_action::AsyncHandlers handlers(*this, c_definitions, out); PerfEventContext ctx(*this, handlers, out); err = setup_output(&ctx); 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; auto map = bytecode_.getMap(MapType::Elapsed); auto ok = map.update_elem(&key, &nsec); if (!ok) { LOG(ERROR) << "Failed to write start time to elapsed map: " << ok.takeError(); return -1; } } int num_begin_end_attached = 0; int num_signal_attached = 0; 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; LOG(V1) << "Attaching 'begin' probe"; ++num_begin_end_attached; } if (resources.special_probes.contains("end")) { ++num_begin_end_attached; } if (run_benchmarks_) { for (auto &probe : resources.benchmark_probes) { auto &benchmark_prog = bytecode_.getProgramForProbe(probe); // Note: on newer kernels you must provide a data_in buffer at least // ETH_HLEN bytes long to make sure input validation works for // opts. Otherwise, bpf_prog_test_run_opts will return -EINVAL for // BPF_PROG_TYPE_XDP. // // https://github.com/torvalds/linux/commit/6b3d638ca897e099fa99bd6d02189d3176f80a47 constexpr size_t ETH_HLEN = 14; char data_in[ETH_HLEN]; struct ::bpf_test_run_opts opts = {}; opts.sz = sizeof(struct ::bpf_test_run_opts); opts.data_in = data_in; opts.data_size_in = ETH_HLEN; opts.repeat = 1'000'000; if (auto ret = ::bpf_prog_test_run_opts(benchmark_prog.fd(), &opts)) { LOG(ERROR) << "bpf_prog_test_run_opts failed: " << ret; return -1; } benchmark_results.emplace_back(probe.path, opts.duration); } } else { for (auto &probe : resources.signal_probes) { auto &sig_prog = bytecode_.getProgramForProbe(probe); sigusr1_prog_fds_.emplace_back(sig_prog.fd()); ++num_signal_attached; } if (child_ && has_usdt_) { try { child_->run(true); } catch (const std::exception &e) { LOG(ERROR) << "Failed to setup child: " << e.what(); return -1; } } bytecode_.attach_external(); // 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 ap = attach_probe(probe, bytecode_); if (!ap) { if (config_->missing_probes == ConfigMissingProbes::error) { return -1; } } else { attached_probes_.push_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 ap = attach_probe(probe, bytecode_); if (!ap) { if (config_->missing_probes == ConfigMissingProbes::error) { return -1; } } else { attached_probes_.push_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; } } } size_t num_attached = 0; for (auto &ap : attached_probes_) { num_attached += ap->probe_count(); } auto total_attached = num_attached + num_begin_end_attached + num_signal_attached + benchmark_results.size(); if (total_attached == 0) { LOG(ERROR) << "Attachment failed for all probes."; return -1; } if (!bt_quiet) out.attached_probes(total_attached); // 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 { bool should_drain = (num_begin_end_attached > 0 || run_benchmarks_) && num_signal_attached == 0 && num_attached == 0; poll_output(out, should_drain); } #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; LOG(V1) << "Attaching 'end' probe"; } poll_output(out, /* drain */ true); uint64_t total_lost_events = bytecode_.get_event_loss_counter(*this, max_cpu_id_); if (total_lost_events > 0) { // We incrementally log lost event counts to stdout via `output` // so users can get a record of it in their txt/json output // but let's log to stderr here to make sure this message doesn't // get lost to users in scripts with high frequency output LOG(WARNING) << "Total lost event count: " << total_lost_events; } return 0; } int BPFtrace::setup_output(void *ctx) { setup_ringbuf(ctx); if (resources.using_skboutput) { return setup_skboutput_perf_buffer(ctx); } return 0; } int BPFtrace::setup_skboutput_perf_buffer(void *ctx) { epollfd_ = epoll_create1(EPOLL_CLOEXEC); if (epollfd_ == -1) { LOG(ERROR) << "Failed to create epollfd"; return -1; } std::vector cpus = util::get_online_cpus(); online_cpus_ = cpus.size(); for (int cpu : cpus) { void *reader = bpf_open_perf_buffer(&perf_event_printer, &perf_event_lost, ctx, -1, cpu, config_->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)); auto map = bytecode_.getMap(MapType::PerfEvent); auto ok = map.update_elem(&cpu, &reader_fd); if (!ok) { LOG(ERROR) << "Failed to update perf event map: " << ok.takeError(); return -1; } 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(void *ctx) { ringbuf_ = ring_buffer__new( bytecode_.getMap(MapType::Ringbuf).fd(), ringbuf_printer, ctx, nullptr); } void BPFtrace::teardown_output() { ring_buffer__free(ringbuf_); if (resources.using_skboutput) // Calls perf_reader_free() on all open perf buffers. open_perf_buffers_.clear(); } void BPFtrace::poll_output(output::Output &out, bool drain) { int ready; bool poll_skboutput = resources.using_skboutput; bool do_poll_ringbuf = true; 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 (poll_skboutput && epollfd_ < 0) { LOG(ERROR) << "Invalid epollfd " << epollfd_; return; } while (true) { if (poll_skboutput) { ready = poll_skboutput_events(); if (should_retry(ready)) { if (!do_poll_ringbuf) continue; } if (should_stop(ready)) { poll_skboutput = false; } } // Handle lost events, if any poll_event_loss(out); 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 (!poll_skboutput && !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; for (auto fd : sigusr1_prog_fds_) { if (::bpf_prog_test_run_opts(fd, nullptr)) { LOG(ERROR) << "Failed to run signal probe"; return; } LOG(V1) << "Attaching 'self:signal' probe"; } } } } int BPFtrace::poll_skboutput_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::poll_event_loss(output::Output &out) { uint64_t current_value = bytecode_.get_event_loss_counter(*this, max_cpu_id_); 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_; } } 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 = util::split_string(cmd_, ' ', /* remove_empty */ true); assert(!args.empty()); return util::resolve_binary_path(args[0]).front(); } else if (pid().has_value()) return "/proc/" + std::to_string(pid().value_or(0)) + "/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 = stackid, .nr_stack_frames = nr_stack_frames }; auto stack_trace = std::vector(stack_type.limit); auto map = bytecode_.getMap(stack_type.name()); auto ok = map.lookup_elem(&stack_key, stack_trace.data()); if (!ok) { LOG(ERROR) << "failed to look up stack id: " << stackid << " stack length: " << nr_stack_frames << " (pid " << pid << "): " << ok.takeError(); return ""; } std::ostringstream stack; std::string padding(indent, ' '); stack << "\n"; for (uint32_t i = 0; i < nr_stack_frames;) { uint64_t addr = stack_trace.at(i); if (stack_type.mode == StackMode::raw) { stack << std::hex << addr << std::endl; ++i; continue; } std::vector syms; if (!ustack) syms = resolve_ksym_stack(addr, true, stack_type.mode == StackMode::perf, config_->show_debug_info); else syms = resolve_usym_stack(addr, pid, probe_id, true, stack_type.mode == StackMode::perf, config_->show_debug_info); std::string sym; for (size_t sym_idx = 0; i < nr_stack_frames && sym_idx < syms.size();) { sym = syms.at(sym_idx); 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; } ++i; ++sym_idx; } } 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 = util::split_string(line, ':'); if (fields.size() >= 3 && fields[2] == uid) { found = true; username = fields[0]; } } file.close(); return username; } std::chrono::time_point BPFtrace::resolve_timestamp( uint32_t mode, uint64_t nsecs) { std::chrono::time_point t; auto ts_mode = static_cast(mode); if (ts_mode == TimestampMode::boot) { if (!boottime_) { LOG(ERROR) << "Cannot resolve timestamp due to failed boot time calculation"; } else { t += std::chrono::seconds(boottime_->tv_sec); t += std::chrono::duration_cast( std::chrono::nanoseconds(boottime_->tv_nsec)); } } t += std::chrono::duration_cast( std::chrono::nanoseconds(nsecs)); return t; } std::string BPFtrace::format_timestamp( const std::chrono::time_point &time_point, uint32_t strftime_id) { return format_timestamp(time_point, resources.strftime_args[strftime_id], false); } std::string BPFtrace::format_timestamp( const std::chrono::time_point &time_point, const std::string &raw_fmt, bool utc) { static const auto nsec_regex = std::regex("%k"); static const auto usec_regex = std::regex("%f"); static const auto msec_regex = std::regex("%l"); time_t time = std::chrono::system_clock::to_time_t(time_point); auto ns = (time_point - std::chrono::floor(time_point)) .count(); if (!time) { return "(?)"; } // Calculate and localize timestamp struct tm tmp; if (utc) { if (!gmtime_r(&time, &tmp)) { LOG(ERROR) << "gmtime_r: " << strerror(errno); return "(?)"; } } else { if (!localtime_r(&time, &tmp)) { LOG(ERROR) << "localtime_r: " << strerror(errno); return "(?)"; } } // Process strftime() format string extensions std::chrono::nanoseconds ns_rem(ns); char nsecs_buf[10]; snprintf(nsecs_buf, sizeof(nsecs_buf), "%09" PRIu64, ns_rem.count()); char usecs_buf[7]; snprintf( usecs_buf, sizeof(usecs_buf), "%06" PRIu64, std::chrono::duration_cast(ns_rem).count()); char msecs_buf[4]; snprintf( msecs_buf, sizeof(msecs_buf), "%03" PRIu64, std::chrono::duration_cast(ns_rem).count()); auto fmt = std::regex_replace(raw_fmt, usec_regex, usecs_buf); fmt = std::regex_replace(fmt, nsec_regex, nsecs_buf); fmt = std::regex_replace(fmt, msec_regex, msecs_buf); const auto timestr_size = config_->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_ksym(uint64_t addr) { auto syms = resolve_ksym_stack(addr, false, false, false); assert(syms.size() == 1); return syms.front(); } std::vector BPFtrace::resolve_ksym_stack(uint64_t addr, bool show_offset, bool perf_mode, bool show_debug_info) { return ksyms_.resolve(addr, show_offset, perf_mode, show_debug_info); } 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 = util::split_string(line, ' '); if (name == tokens[2]) { addr = read_address_from_output(line); break; } } file.close(); return addr; } static int sym_resolve_callback(const char *name, uint64_t addr, uint64_t size, void *payload) { auto *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 char *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 addr; } std::string BPFtrace::resolve_cgroup_path(uint64_t cgroup_path_id, uint64_t cgroup_id) const { auto paths = util::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 char *inet) { char addr_cstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, inet, addr_cstr, INET_ADDRSTRLEN); return addr_cstr; } static std::string resolve_inetv6(const char *inet) { char addr_cstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, inet, addr_cstr, INET6_ADDRSTRLEN); return addr_cstr; } std::string BPFtrace::resolve_inet(int af, const char *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) { auto syms = resolve_usym_stack(addr, pid, probe_id, false, false, false); assert(syms.size() == 1); return syms.front(); } std::vector BPFtrace::resolve_usym_stack(uint64_t addr, int32_t pid, int32_t probe_id, bool show_offset, bool perf_mode, bool show_debug_info) { std::string pid_exe; auto res = util::get_pid_exe(pid); if (res) { pid_exe = *res; } else if (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 && !util::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, perf_mode, show_debug_info); } std::string BPFtrace::resolve_probe(uint64_t probe_id) const { assert(probe_id < resources.probe_ids.size()); return resources.probe_ids[probe_id]; } const util::FuncsModulesMap &BPFtrace::get_traceable_funcs() const { if (traceable_funcs_.empty()) traceable_funcs_ = util::parse_traceable_funcs(); return traceable_funcs_; } const util::FuncsModulesMap &BPFtrace::get_raw_tracepoints() const { if (raw_tracepoints_.empty()) raw_tracepoints_ = util::parse_rawtracepoints(); return raw_tracepoints_; } bool BPFtrace::is_traceable_func(const std::string &func_name) const { const auto &funcs = get_traceable_funcs(); return funcs.contains(func_name); } int BPFtrace::resume_tracee(pid_t tracee_pid) { return ::kill(tracee_pid, SIGCONT); } std::unordered_set BPFtrace::get_func_modules( const std::string &func_name) const { const auto &funcs = get_traceable_funcs(); auto mod = funcs.find(func_name); return mod != funcs.end() ? mod->second : std::unordered_set(); } std::unordered_set BPFtrace::get_raw_tracepoint_modules( const std::string &name) const { const auto &rts = get_raw_tracepoints(); auto mod = rts.find(name); return mod != rts.end() ? mod->second : std::unordered_set(); } const std::optional &BPFtrace::get_pidns_self_stat() const { static std::optional pidns = []() -> std::optional { struct stat s; if (::stat("/proc/self/ns/pid", &s)) { if (errno == ENOENT) return std::nullopt; 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_.contains(file)) { 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, const OpaqueValue &pkt) { 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.data(), pkt.size()); } void BPFtrace::parse_module_btf(const std::set &modules) { btf_->load_module_btfs(modules); } bool BPFtrace::has_btf_data() const { return btf_->has_data(); } // 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, tracepoints, // and raw tracepoints std::set BPFtrace::list_modules(const ast::ASTContext &ctx) { std::set modules; for (const auto &probe : ctx.root->probes) { for (const auto &ap : probe->attach_points) { auto probe_type = probetype(ap->provider); if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit || probe_type == ProbeType::rawtracepoint || probe_type == ProbeType::kprobe || probe_type == ProbeType::kretprobe) { // Force module name into listing bool clear_target; if (ap->target.empty()) { ap->target = "*"; clear_target = true; } else { clear_target = false; } for (std::string match : probe_matcher_->get_matches_for_ap(*ap)) { auto module = util::erase_prefix(match); modules.insert(module); } if (clear_target) ap->target.clear(); } 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.24.1/src/bpftrace.h000066400000000000000000000223451506776124200161320ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "ast/ast.h" #include "ast/pass_manager.h" #include "ast/passes/clang_parser.h" #include "ast/passes/probe_expansion.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/output.h" #include "pcap_writer.h" #include "probe_matcher.h" #include "procmon.h" #include "required_resources.h" #include "struct.h" #include "types.h" #include "usyms.h" #include "util/cpus.h" #include "util/kernel.h" #include "util/result.h" namespace bpftrace { using util::symbol; const int timeout_ms = 100; struct stack_key { int64_t stackid; int64_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 { Parse, Ast, Codegen, CodegenOpt, Disassemble, Libbpf, Verifier, Types, }; const std::unordered_map debug_stages = { // clang-format off { "parse", DebugStage::Parse }, { "ast", DebugStage::Ast }, { "codegen", DebugStage::Codegen }, { "codegen-opt", DebugStage::CodegenOpt }, #ifndef NDEBUG { "dis", DebugStage::Disassemble }, #endif { "libbpf", DebugStage::Libbpf }, { "verifier", DebugStage::Verifier }, { "types", DebugStage::Types }, // clang-format on }; class WildcardException : public std::exception { public: WildcardException(std::string msg) : msg_(std::move(msg)) { } const char *what() const noexcept override { return msg_.c_str(); } private: std::string msg_; }; class BPFtrace : public ast::State<"bpftrace"> { public: BPFtrace(BPFnofeature no_feature = BPFnofeature(), std::unique_ptr config = std::make_unique()) : btf_(std::make_unique(this)), feature_(std::make_unique(no_feature, *btf_)), probe_matcher_(std::make_unique(this)), ncpus_(util::get_possible_cpus().size()), max_cpu_id_(util::get_max_cpu_id()), config_(std::move(config)), ksyms_(*config_), usyms_(*config_) { } ~BPFtrace() override; virtual int add_probe(const ast::AttachPoint &ap, const ast::Probe &p, ast::ExpansionType expansion, std::set expanded_funcs, int usdt_location_idx = 0); Probe generateWatchpointSetupProbe(const ast::AttachPoint &ap, const ast::Probe &probe); int num_probes() const; int prerun() const; int run(output::Output &out, const ast::CDefinitions &c_definitions, BpfBytecode bytecode); virtual Result> attach_probe( Probe &probe, const BpfBytecode &bytecode); int run_iter(); 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_ksym(uint64_t addr); std::string resolve_usym(uint64_t addr, int32_t pid, int32_t probe_id); std::string resolve_inet(int af, const char *inet) const; std::string resolve_uid(uint64_t addr) const; std::chrono::time_point resolve_timestamp( uint32_t mode, uint64_t nsecs); std::string format_timestamp( const std::chrono::time_point &time_point, uint32_t strftime_id); std::string format_timestamp( const std::chrono::time_point &time_point, const std::string &raw_fmt, bool utc); time_t time_since_epoch(uint32_t mode, uint64_t timestamp_ns, 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 char *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; void add_param(const std::string ¶m); std::string get_param(size_t index) const; size_t num_params() const; void request_finalize(); std::optional get_watchpoint_binary_path() const; virtual bool is_traceable_func(const std::string &func_name) const; virtual int resume_tracee(pid_t tracee_pid); virtual std::unordered_set get_func_modules( const std::string &func_name) const; virtual std::unordered_set get_raw_tracepoint_modules( const std::string &name) const; virtual const std::optional &get_pidns_self_stat() const; bool write_pcaps(uint64_t id, uint64_t ns, const OpaqueValue &pkt); void parse_module_btf(const std::set &modules); bool has_btf_data() const; Dwarf *get_dwarf(const std::string &filename); Dwarf *get_dwarf(const ast::AttachPoint &attachpoint); std::set list_modules(const ast::ASTContext &ctx); 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; // For each helper, list of all generated call sites. std::map> helper_use_loc_; const util::FuncsModulesMap &get_traceable_funcs() const; const util::FuncsModulesMap &get_raw_tracepoints() const; util::KConfig kconfig; std::vector> attached_probes_; std::vector sigusr1_prog_fds_; unsigned int join_argnum_ = 16; unsigned int join_argsize_ = 1024; std::unique_ptr out_; std::unique_ptr btf_; std::unique_ptr feature_; 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_; bool need_recursion_check_ = false; std::unique_ptr probe_matcher_; std::unordered_set btf_set_; std::unique_ptr child_; std::unique_ptr procmon_; std::optional pid() const { if (procmon_) { return procmon_->pid(); } return std::nullopt; } int ncpus_; int online_cpus_; int max_cpu_id_; std::unique_ptr config_; bool run_benchmarks_ = false; std::vector> benchmark_results; private: Ksyms ksyms_; Usyms usyms_; std::vector params_; std::vector> open_perf_buffers_; std::map> pcap_writers_; int create_pcaps(); void close_pcaps(); int setup_output(void *ctx); int setup_skboutput_perf_buffer(void *ctx); void setup_ringbuf(void *ctx); std::vector resolve_ksym_stack(uint64_t addr, bool show_offset, bool perf_mode, bool show_debug_info); std::vector resolve_usym_stack(uint64_t addr, int32_t pid, int32_t probe_id, bool show_offset, bool perf_mode, bool show_debug_info); void teardown_output(); void poll_output(output::Output &out, bool drain = false); int poll_skboutput_events(); void poll_event_loss(output::Output &out); 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, ast::ExpansionType expansion, std::set expanded_funcs, 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 util::FuncsModulesMap traceable_funcs_; mutable util::FuncsModulesMap raw_tracepoints_; std::unordered_map> dwarves_; }; } // namespace bpftrace bpftrace-0.24.1/src/btf.cpp000066400000000000000000001031271506776124200154500ustar00rootroot00000000000000#include "scopeguard.h" #include #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 "arch/arch.h" #include "ast/context.h" #include "ast/pass_manager.h" #include "bpftrace.h" #include "btf.h" #include "log.h" #include "tracefs/tracefs.h" #include "types.h" namespace bpftrace { static __u32 type_cnt(const struct btf *btf) { const auto count = btf__type_cnt(btf); return count ? count - 1 : 0; } __u32 BTF::start_id(const struct btf *btf) const { return btf == vmlinux_btf ? 1 : vmlinux_btf_size + 1; } BTF::BTF() : BTF(nullptr) { } BTF::BTF(BPFtrace *bpftrace) : bpftrace_(bpftrace) { } BTF::~BTF() { for (auto &btf_obj : btf_objects) btf__free(btf_obj.btf); } void BTF::load_vmlinux_btf() { if (state != INIT) { // Don't attempt to reload vmlinux even if it fails below return; } state = ERROR; // 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), .name = "" }); vmlinux_btf = btf_objects.back().btf; if (!vmlinux_btf) { LOG(WARNING) << "BTF: failed to parse BTF from " << path; return; } } else { 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, .name = "vmlinux" }); } if (btf_objects.empty()) { LOG(V1) << "BTF: failed to find BTF data"; return; } vmlinux_btf_size = type_cnt(vmlinux_btf); state = VMLINUX_LOADED; } bool BTF::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_; } void BTF::load_module_btfs(const std::set &modules) { load_vmlinux_btf(); if ((bpftrace_ && !has_module_btf()) || state != VMLINUX_LOADED) 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) { if (bpf_btf_get_next_id(id, &id)) { 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); uint32_t info_len = sizeof(info); auto 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; } if (!info.kernel_btf) continue; if (modules.contains(name)) { btf_objects.push_back( BTFObj{ .btf = btf__load_from_kernel_by_id_split(id, vmlinux_btf), .name = name }); } } state = VMLINUX_AND_MODULES_LOADED; } static void dump_printf(void *ctx, const char *fmt, va_list args) { auto *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, std::stringstream *ctx) { return btf_dump__new(btf, dump_printf, static_cast(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_view btf_type_str(std::string_view type) { using namespace std::literals::string_view_literals; if (type.starts_with("struct ")) return type.substr("struct "sv.length()); if (type.starts_with("union ")) return type.substr("union "sv.length()); if (type.starts_with("enum ")) return type.substr("enum "sv.length()); return type; } std::string BTF::dump_defs_from_btf( const struct btf *btf, std::unordered_set &types) const { std::stringstream ret; auto *dump = dump_new(btf, dump_printf, &ret); if (auto err = libbpf_get_error(dump)) { char err_buf[256] = {}; libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } SCOPE_EXIT { btf_dump__free(dump); }; // 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) bool all = types.empty(); __u32 max = type_cnt(btf); for (__u32 id = 1; id <= max && (all || !types.empty()); id++) { const auto *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 auto *p = btf_enum(t); for (__u16 e = 0, vlen = btf_vlen(t); e < vlen; ++e, ++p) { if (all || types.erase(btf_str(btf, p->name_off))) { btf_dump__dump_type(dump, id); break; } } } if (all || types.erase(full_type_str(btf, t))) { btf_dump__dump_type(dump, id); } } return ret.str(); } std::string BTF::c_def(const std::unordered_set &set) { if (!has_data()) return {}; // 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(std::string_view name, std::string_view field) { if (!has_data()) return {}; auto btf_name = btf_type_str(name); auto type_id = find_id(btf_name); if (!type_id.btf) return {}; return type_of(type_id, field); } std::string BTF::type_of(const BTFId &type_id, std::string_view field) { if (!has_data()) return {}; 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 {}; // 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.empty()) { 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 {}; } return full_type_str(type_id.btf, f); } return {}; } 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) { // Note that we allocate an extra element in this case, since it is // necessary for the frontend to detect when a well-formed string is // stored versus a non-well-formed string. We consider the array to be // well-formed even if it does not contain a NUL-terminator, since it has // a known size up front. However, our string must store the terminator // to signal that it is well-formed. stype = CreateString(array->nelems + 1); } else { stype = CreateArray(array->nelems, elem_type); } } return stype; } std::shared_ptr BTF::resolve_args(std::string_view func, bool ret, bool check_traceable, bool skip_first_arg, std::string &err) { if (!has_data()) { err = "BTF data not available"; return nullptr; } auto func_id = find_id(func, BTF_KIND_FUNC); if (!func_id.btf) { err = "no BTF data for " + std::string(func); return nullptr; } 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 = std::string(func) + " is not a function"; return nullptr; } if (check_traceable) { if (bpftrace_ && !bpftrace_->is_traceable_func(std::string(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 nullptr; } } const struct btf_param *p = btf_params(t); __u16 vlen = btf_vlen(t); if (vlen >= arch::Host::arguments().size()) { err = "functions with more than 6 parameters are " "not supported."; return nullptr; } int arg_idx = 0; auto args = std::make_shared(0, false); for (__u16 j = 0; j < vlen; j++, p++) { if (j == 0 && skip_first_arg) { continue; } const char *str = btf_str(func_id.btf, p->name_off); if (!str) { err = "failed to resolve arguments"; return nullptr; } 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. auto 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::shared_ptr BTF::resolve_raw_tracepoint_args(std::string_view func, std::string &err) { for (const auto &prefix : RT_BTF_PREFIXES) { if (auto args = resolve_args( std::string(prefix) + std::string(func), false, true, true, err)) { return args; } } return nullptr; } std::string BTF::get_all_funcs_from_btf(const BTFObj &btf_obj) const { std::string funcs; auto id = start_id(btf_obj.btf), max = type_cnt(btf_obj.btf); for (; 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; std::string func_name = btf__name_by_offset(btf_obj.btf, t->name_off); 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::Host::arguments().size()) continue; funcs += btf_obj.name + ":" + func_name + "\n"; } return funcs; } std::unique_ptr BTF::get_all_funcs() { if (!all_funcs_.empty()) { return std::make_unique(all_funcs_); } for (const auto &btf_obj : btf_objects) all_funcs_ += get_all_funcs_from_btf(btf_obj); return std::make_unique(all_funcs_); } std::string BTF::get_all_raw_tracepoints_from_btf(const BTFObj &btf_obj) const { std::set func_set; auto id = start_id(btf_obj.btf), max = type_cnt(btf_obj.btf); for (; 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; std::string_view func_name = btf__name_by_offset(btf_obj.btf, t->name_off); 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 (btf_vlen(t) >= arch::Host::arguments().size()) continue; bool found = false; for (const auto &prefix : RT_BTF_PREFIXES) { if (func_name.starts_with(prefix)) { found = true; func_name.remove_prefix(prefix.length()); break; } } if (!found) continue; // TODO: remove the std::string on C++26 func_set.insert(btf_obj.name + ':' + std::string(func_name) + '\n'); } std::string funcs; for (const auto &func : func_set) { funcs += func; } return funcs; } std::unique_ptr BTF::get_all_raw_tracepoints() { if (!all_rawtracepoints_.empty()) { return std::make_unique(all_rawtracepoints_); } for (const auto &btf_obj : btf_objects) all_rawtracepoints_ += get_all_raw_tracepoints_from_btf(btf_obj); return std::make_unique(all_rawtracepoints_); } FuncParamLists BTF::get_params_from_btf( const BTFObj &btf_obj, const std::set &funcs) const { std::stringstream type; auto *dump = dump_new(btf_obj.btf, dump_printf, &type); if (auto err = libbpf_get_error(dump)) { char err_buf[256] = {}; libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } SCOPE_EXIT { btf_dump__free(dump); }; FuncParamLists params; auto id = start_id(btf_obj.btf), max = type_cnt(btf_obj.btf); for (; 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 auto func_name = btf_obj.name + ":" + btf__name_by_offset(btf_obj.btf, t->name_off); if (!funcs.contains(func_name)) 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 auto *p = btf_params(t); for (__u16 j = 0, len = btf_vlen(t); j < len; j++, p++) { const char *arg_name = btf__name_by_offset(btf_obj.btf, p->name_off); // set by dump_printf callback type.str(""); if (btf_dump__emit_type_decl(dump, p->type, &decl_opts)) { LOG(ERROR) << "failed to dump argument: " << arg_name; break; } params[func_name].push_back(type.str() + " " + arg_name); } if (!t->type) continue; // set by dump_printf callback type.str(""); if (btf_dump__emit_type_decl(dump, t->type, &decl_opts)) { LOG(ERROR) << "failed to dump return type for: " << func_name; break; } params[func_name].push_back(type.str() + " retval"); } if (id != (max + 1)) LOG(ERROR) << "BTF data inconsistency " << id << "," << max; return params; } FuncParamLists BTF::get_kprobes_params_from_btf( const BTFObj &btf_obj, const std::set &funcs, bool is_kretprobe) const { std::stringstream type; auto *dump = dump_new(btf_obj.btf, dump_printf, &type); if (auto err = libbpf_get_error(dump)) { char err_buf[256] = {}; libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(V1) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } SCOPE_EXIT { btf_dump__free(dump); }; FuncParamLists params; auto id = start_id(btf_obj.btf), max = type_cnt(btf_obj.btf); for (; 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; std::string func_name; const std::string pure_func_name = btf__name_by_offset(btf_obj.btf, t->name_off); const std::string obj_func_name = btf_obj.name + ":" + pure_func_name; // First match the module prefix name, then match the pure function name. // For example, first match "kprobe:vmlinux:do_sys*", then "kprobe:do_sys*". // The same goes for other modules, such as "kvm". if (funcs.contains(obj_func_name)) func_name = obj_func_name; else if (funcs.contains(pure_func_name)) func_name = pure_func_name; else 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 = ""); if (!is_kretprobe) { const auto *p = btf_params(t); for (__u16 j = 0, argN = 0, len = btf_vlen(t); j < len; j++, p++) { const std::string arg_name = "arg" + std::to_string(argN); // set by dump_printf callback type.str(""); if (btf_dump__emit_type_decl(dump, p->type, &decl_opts)) { LOG(V1) << "failed to dump argument: " << arg_name; break; } // Note that floating point arguments are typically passed in special // registers which don’t count as argN arguments. 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 const auto *pt = btf__type_by_id(btf_obj.btf, p->type); if (pt && BTF_INFO_KIND(pt->info) == BTF_KIND_FLOAT) continue; params[func_name].push_back(type.str() + " " + arg_name); argN++; } } else { if (!t->type) { params[func_name].emplace_back("void"); continue; } // set by dump_printf callback type.str(""); if (btf_dump__emit_type_decl(dump, t->type, &decl_opts)) { LOG(ERROR) << "failed to dump return type for: " << func_name; break; } params[func_name].push_back(type.str() + " retval"); } } if (id != (max + 1)) LOG(BUG) << "BTF data inconsistency " << id << "," << max; return params; } FuncParamLists BTF::get_raw_tracepoints_params_from_btf( const BTFObj &btf_obj, const std::set &rawtracepoints) const { std::stringstream type; auto *dump = dump_new(btf_obj.btf, dump_printf, &type); if (auto err = libbpf_get_error(dump)) { char err_buf[256] = {}; libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } SCOPE_EXIT { btf_dump__free(dump); }; FuncParamLists params; auto id = start_id(btf_obj.btf), max = type_cnt(btf_obj.btf); for (; 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; std::string_view tp_name = btf__name_by_offset(btf_obj.btf, t->name_off); for (const auto &prefix : RT_BTF_PREFIXES) { if (tp_name.starts_with(prefix)) tp_name.remove_prefix(prefix.length()); } // Checking multiple prefixes so make sure we don't add duplicates // TODO: remove the std::string ctor on C++26 auto mod_tp_name = btf_obj.name + ":" + std::string(tp_name); if (!rawtracepoints.contains(mod_tp_name) || params.contains(mod_tp_name)) 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 = btf_params(t); for (__u16 j = 0, len = btf_vlen(t); j < len; j++, p++) { if (j == 0) { // The first param is void *, which is not really part of the // rawtracepoint params continue; } const char *arg_name = btf__name_by_offset(btf_obj.btf, p->name_off); // set by dump_printf callback type.str(""); if (btf_dump__emit_type_decl(dump, p->type, &decl_opts)) { LOG(ERROR) << "failed to dump argument: " << arg_name; break; } params[mod_tp_name].push_back(type.str() + " " + arg_name); } } if (id != (max + 1)) LOG(ERROR) << "BTF data inconsistency " << id << "," << max; return params; } FuncParamLists BTF::get_params_impl( const std::set &funcs, std::function &funcs)> get_param_btf_cb) const { FuncParamLists params; auto all_resolved = [¶ms](const std::string &f) { return params.contains(f); }; for (const auto &btf_obj : btf_objects) { if (std::ranges::all_of(funcs, all_resolved)) break; auto mod_params = get_param_btf_cb(btf_obj, funcs); params.insert(mod_params.begin(), mod_params.end()); } return params; } FuncParamLists BTF::get_params(const std::set &funcs) const { return get_params_impl( funcs, [this](const BTFObj &btf_obj, const std::set &funcs) { return get_params_from_btf(btf_obj, funcs); }); } FuncParamLists BTF::get_kprobes_params(const std::set &funcs) const { return get_params_impl( funcs, [this](const BTFObj &btf_obj, const std::set &funcs) { return get_kprobes_params_from_btf(btf_obj, funcs, false); }); } FuncParamLists BTF::get_kretprobes_params( const std::set &funcs) const { return get_params_impl( funcs, [this](const BTFObj &btf_obj, const std::set &funcs) { return get_kprobes_params_from_btf(btf_obj, funcs, true); }); } FuncParamLists BTF::get_rawtracepoint_params( const std::set &rawtracepoints) const { return get_params_impl( rawtracepoints, [this](const BTFObj &btf_obj, const std::set &funcs) { return get_raw_tracepoints_params_from_btf(btf_obj, funcs); }); } std::set BTF::get_all_structs_from_btf(const struct btf *btf) const { std::set struct_set; std::stringstream types; auto *dump = dump_new(btf, dump_printf, &types); if (auto err = libbpf_get_error(dump)) { char err_buf[256] = { 0 }; libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } SCOPE_EXIT { btf_dump__free(dump); }; auto id = start_id(btf), max = type_cnt(btf); for (; 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(std::move(name)); } if (id != (max + 1)) LOG(ERROR) << " BTF data inconsistency " << id << "," << max; if (bt_verbose) { // BTF dump contains definitions of all types in a single string, here we // split it std::istringstream type_stream(types.str()); 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 (const 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 { constexpr std::string_view prefix = "bpf_iter__"; // kernel commit 6fcd486b3a0a("bpf: Refactor RCU enforcement in the // verifier.") add 'struct bpf_iter__task__safe_trusted' constexpr std::string_view suffix___safe_trusted = "__safe_trusted"; std::unordered_set iter_set; auto id = start_id(btf), max = type_cnt(btf); for (; id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); if (!t || !btf_is_struct(t)) continue; std::string_view name = btf_str(btf, t->name_off); // skip __safe_trusted suffix struct if (name.ends_with(suffix___safe_trusted)) continue; if (name.starts_with(prefix)) { name.remove_prefix(prefix.length()); iter_set.insert(std::string(name)); } } return iter_set; } std::unordered_set BTF::get_all_iters() const { std::unordered_set iters; for (const 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, __u32 kind) const { for (const auto &btf_obj : btf_objects) { if (!mod.empty() && mod != btf_obj.name) continue; auto id = find_id_in_btf(btf_obj.btf, func, kind); if (id >= 0) return id; } return -1; } BTF::BTFId BTF::find_id(std::string_view name, std::optional<__u32> kind) const { for (const auto &btf_obj : btf_objects) { __s32 id = kind ? btf__find_by_name_kind(btf_obj.btf, name.data(), *kind) : btf__find_by_name(btf_obj.btf, name.data()); if (id >= 0) return { .btf = btf_obj.btf, .id = static_cast<__u32>(id) }; } return { .btf = nullptr, .id = 0 }; } __s32 BTF::find_id_in_btf(struct btf *btf, std::string_view name, std::optional<__u32> kind) const { for (auto id = start_id(btf), max = 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; const auto *type_name = btf__name_by_offset(btf, t->name_off); if (name == type_name) return id; } return -1; } void BTF::resolve_fields(const 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, std::move(record), 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, std::shared_ptr record, __u32 start_offset) { const 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 }; const 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(std::string_view 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(std::string_view 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 }); } ast::Pass CreateParseBTFPass() { return ast::Pass::create( "btf", []([[maybe_unused]] ast::ASTContext &ast, BPFtrace &b) { b.parse_module_btf(b.list_modules(ast)); }); } } // namespace bpftrace bpftrace-0.24.1/src/btf.h000066400000000000000000000163301506776124200151140ustar00rootroot00000000000000#pragma once #include "types.h" #include #include #include #include #include #include #include #include #include #include "ast/pass_manager.h" // 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 { // Note: there are several prefixes for raw tracepoint BTF functions. // "__probestub_" seems to be the most accurate in terms of getting the params // but it wasn't added until May 2023 so older kernels might not have it, // which is why we also check "__traceiter_" (as needed). // "btf_trace_" prefix, which is what the kernel uses for raw tracepoints, we // use in bpfprogram.cpp to validate if we can attach to this raw tracepoint. // The BTF for "btf_trace_" is a typedef that eventually resolves to a // FUNC_PROTO but the params for this do not have names, which is what we need. // "__probestub_" was added here: // https://lore.kernel.org/all/168507471874.913472.17214624519622959593.stgit@mhiramat.roam.corp.google.com/ // "__traceiter_" was added here: // https://lore.kernel.org/all/20200908105743.GW2674@hirez.programming.kicks-ass.net/ static const std::vector RT_BTF_PREFIXES = { "__probestub_", "__traceiter_" }; class BPFtrace; using FuncParamLists = std::map>; class BTF { enum state { INIT, ERROR, VMLINUX_LOADED, VMLINUX_AND_MODULES_LOADED, }; // BTF object for vmlinux or a kernel module. // We're currently storing its name and BTF id. struct BTFObj { struct btf* btf; 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(); BTF(BPFtrace* bpftrace); ~BTF(); bool has_data(); bool has_module_btf(); bool modules_loaded() const; size_t objects_cnt() const { return btf_objects.size(); } void load_module_btfs(const std::set& modules); std::string type_of(std::string_view name, std::string_view field); std::string type_of(const BTFId& type_id, std::string_view field); SizedType get_stype(std::string_view type_name); SizedType get_var_type(std::string_view var_name); // Returns a string containing the C definitions generated from the BTF data. // If `set` is provided (non-empty), then the generated types will be limited // to just those in the set. If `set` is not provided (empty), then all types // will be generated. std::string c_def(const std::unordered_set& set = {}); std::set get_all_structs() const; std::unique_ptr get_all_funcs(); std::unordered_set get_all_iters() const; std::unique_ptr get_all_raw_tracepoints(); FuncParamLists get_params(const std::set& funcs) const; FuncParamLists get_kprobes_params(const std::set& funcs) const; FuncParamLists get_kretprobes_params( const std::set& funcs) const; FuncParamLists get_rawtracepoint_params( const std::set& rawtracepoints) const; std::shared_ptr resolve_args(std::string_view func, bool ret, bool check_traceable, bool skip_first_arg, std::string& err); std::shared_ptr resolve_raw_tracepoint_args(std::string_view func, std::string& err); void resolve_fields(const SizedType& type); int get_btf_id(std::string_view func, std::string_view mod, __u32 kind = BTF_KIND_FUNC) const; private: void load_vmlinux_btf(); SizedType get_stype(const BTFId& btf_id, bool resolve_structs = true); void resolve_fields(const BTFId& type_id, std::shared_ptr 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(std::string_view name, std::optional<__u32> kind = std::nullopt) const; __s32 find_id_in_btf(struct btf* btf, std::string_view name, std::optional<__u32> kind = 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::string get_all_raw_tracepoints_from_btf(const BTFObj& btf_obj) const; FuncParamLists get_params_impl( const std::set& funcs, std::function& funcs)> get_param_btf_cb) const; FuncParamLists get_params_from_btf(const BTFObj& btf_obj, const std::set& funcs) const; FuncParamLists get_kprobes_params_from_btf(const BTFObj& btf_obj, const std::set& funcs, bool is_kretprobe) const; FuncParamLists get_raw_tracepoints_params_from_btf( const BTFObj& btf_obj, const std::set& rawtracepoints) 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; __u32 start_id(const struct btf* btf) const; struct btf* vmlinux_btf = nullptr; __u32 vmlinux_btf_size; // BTF objects for vmlinux and modules std::vector btf_objects; enum state state = INIT; BPFtrace* bpftrace_ = nullptr; std::string all_funcs_; std::string all_rawtracepoints_; std::optional has_module_btf_; }; inline bool BTF::has_data() { // This can be called multiple times and won't reload vmlinux load_vmlinux_btf(); return state == VMLINUX_LOADED || state == VMLINUX_AND_MODULES_LOADED; } inline bool BTF::modules_loaded() const { return state == VMLINUX_AND_MODULES_LOADED; } ast::Pass CreateParseBTFPass(); } // namespace bpftrace bpftrace-0.24.1/src/btf/000077500000000000000000000000001506776124200147405ustar00rootroot00000000000000bpftrace-0.24.1/src/btf/CMakeLists.txt000066400000000000000000000001531506776124200174770ustar00rootroot00000000000000add_library(btf STATIC btf.cpp compat.cpp helpers.cpp irbuilder.cpp ) add_dependencies(btf parser) bpftrace-0.24.1/src/btf/btf.cpp000066400000000000000000000616421506776124200162300ustar00rootroot00000000000000#include #include #include #include "btf/btf.h" #include "btf/helpers.h" #include "libbpf/bpf.h" #include "util/temp.h" namespace bpftrace::btf { using namespace bpftrace; namespace detail { Handle::Handle() : btf_(btf__new_empty()) { } Handle::Handle(std::shared_ptr base) : btf_(btf__new_empty_split(base->btf_)), base_(std::move(base)) { } Result Handle::parse(const void *data, size_t sz) { auto f = util::TempFile::create(); if (!f) { return f.takeError(); } auto ok = f->write_all(std::span(static_cast(data), sz)); if (!ok) { return ok.takeError(); } auto *btf = btf__parse(f->path().c_str(), nullptr); // No extensions. if (btf == nullptr) { return make_error(errno); } return std::make_shared(btf); } Result Handle::parse(HandleRef base, const void *data, size_t sz) { auto f = util::TempFile::create(); if (!f) { return f.takeError(); } auto ok = f->write_all(std::span(static_cast(data), sz)); if (!ok) { return ok.takeError(); } auto *btf = btf__parse_split(f->path().c_str(), base->btf_); if (btf == nullptr) { return make_error(errno); } return std::make_shared(btf, std::move(base)); } Handle::~Handle() { btf__free(btf_); } } // namespace detail char ParseError::ID; void ParseError::log(llvm::raw_ostream &OS) const { OS << "Parse error: " << std::strerror(err_); } char TypeError::ID; void TypeError::log(llvm::raw_ostream &OS) const { OS << "Type error: " << std::strerror(err_); } char UnknownType::ID; void UnknownType::log(llvm::raw_ostream &OS) const { std::visit([&](const auto &v) { OS << "Unknown type: " << v; }, name_); } char KindMismatch::ID; void KindMismatch::log(llvm::raw_ostream &OS) const { OS << "Kind mismatch: got " << got_ << ", expected " << expected_; } Result BaseType::size() const { auto v = btf__resolve_size(btf_library(), type_id_); if (v < 0) { return make_error(v); } return static_cast(v); } // Returns the required alignment for the type. Result BaseType::alignment() const { auto v = btf__align_of(btf_library(), type_id_); if (v < 0) { return make_error(v); } return static_cast(v); } const struct btf_type *BaseType::btf_type() const { return btf__type_by_id(handle_->btf_library(), type_id_); } Result Integer::add(HandleRef handle, const std::string &name, size_t bytes, int encoding) { auto v = btf__add_int(handle->btf_library(), name.c_str(), bytes, encoding); if (v < 0) { return make_error(v); } return Integer(std::move(handle), v); } size_t Integer::bytes() const { return btf_type()->size; } Result Pointer::add(HandleRef handle, const AnyType &type) { auto v = btf__add_ptr(handle->btf_library(), type.type_id()); if (v < 0) { return make_error(v); } return Pointer(std::move(handle), v); } Result Pointer::element_type() const { return AnyType::lookup(handle(), btf_type()->type); } Result Array::add(HandleRef handle, const Integer &index_type, const ValueType &element_type, size_t elements) { auto v = btf__add_array(handle->btf_library(), index_type.type_id(), element_type.type_id(), elements); if (v < 0) { return make_error(v); } return Array(std::move(handle), v); } Result Array::index_type() const { return ValueType::lookup(handle(), btf_array(btf_type())->index_type); } Result Array::element_type() const { return AnyType::lookup(handle(), btf_array(btf_type())->type); } size_t Array::element_count() const { return btf_array(btf_type())->nelems; } static Result forEachField( HandleRef handle, const struct btf_type *t, std::function fn, std::optional<__u16> start_index = std::nullopt, std::optional<__u16> end_index = std::nullopt) { if (!start_index) { start_index = 0; } if (!end_index) { end_index = btf_vlen(t); } auto *members = btf_members(t); for (int i = *start_index; i < *end_index; i++) { std::string name( btf__name_by_offset(handle->btf_library(), members[i].name_off)); auto ok = ValueType::lookup(handle, members[i].type); if (!ok) { // We found the field, but can't resolve the type. return ok.takeError(); } bool kflag = BTF_INFO_KFLAG(t->info); FieldInfo info = { .type = std::move(*ok), .bit_offset = kflag ? BTF_MEMBER_BIT_OFFSET(members[i].offset) : members[i].offset, .bit_size = kflag ? BTF_MEMBER_BITFIELD_SIZE(members[i].offset) : 0, }; if (!fn(std::move(name), std::move(info))) { break; } } return OK(); } Result Struct::add( HandleRef handle, const std::string &name, const std::vector> &fields) { size_t total_bytes = 0; std::vector offsets; for (const auto &[_, t] : fields) { auto align = t.alignment(); if (!align) { return align.takeError(); } auto left = total_bytes % (*align); if (left != 0) { total_bytes += (*align) - left; } offsets.push_back(total_bytes); auto size = t.size(); if (!size) { return size.takeError(); } total_bytes += (*size); } auto v = btf__add_struct(handle->btf_library(), name.c_str(), total_bytes); if (v < 0) { return make_error(v); } size_t index = 0; for (const auto &[field_name, t] : fields) { // We always provide bit_size=0, which indicates that this is not a // bitfield. We cannot construct bitfields via the `add` API. int rc = btf__add_field(handle->btf_library(), field_name.c_str(), t.type_id(), 8 * offsets[index], 0); if (rc < 0) { return make_error(rc); } index++; } return Struct(std::move(handle), v); } std::string Struct::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } Result Struct::field(const std::string &name) const { std::optional result; auto ok = forEachField(handle(), btf_type(), [&](std::string &&field_name, FieldInfo &&info) -> bool { if (name == field_name) { result.emplace(std::move(info)); return false; } return true; }); if (!ok) { return ok.takeError(); } if (!result) { return make_error(name); } return std::move(*result); } Result Struct::field(uint32_t index) const { std::optional result; auto ok = forEachField( handle(), btf_type(), [&]([[maybe_unused]] std::string &&field_name, FieldInfo &&info) -> bool { result.emplace(std::move(info)); return true; }, index, index + 1); if (!ok) { return ok.takeError(); } if (!result) { return make_error(index); } return std::move(*result); } Result>> Struct::fields() const { std::vector> result; auto ok = forEachField( handle(), btf_type(), [&](std::string &&field_name, FieldInfo &&info) -> bool { result.emplace_back(std::move(field_name), std::move(info)); return true; }); if (!ok) { return ok.takeError(); } return result; } Result Union::add( HandleRef handle, const std::string &name, const std::vector> &fields) { size_t total_bytes = 0; for (const auto &[_, t] : fields) { auto size = t.size(); if (!size) { return size.takeError(); } total_bytes = std::max(total_bytes, *size); } auto v = btf__add_union(handle->btf_library(), name.c_str(), total_bytes); if (v < 0) { return make_error(v); } for (const auto &[field_name, t] : fields) { // We always provide bit_offset 0, with a bit_size of 0 because bitfields // are not supported. We only rely on the total size above. auto r = btf__add_field( handle->btf_library(), field_name.c_str(), t.type_id(), 0, 0); if (r < 0) { return make_error(r); } } return Union(std::move(handle), v); } std::string Union::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } Result Union::field(const std::string &name) const { std::optional result; auto ok = forEachField(handle(), btf_type(), [&](std::string &&field_name, FieldInfo &&info) -> bool { if (name == field_name) { result.emplace(std::move(info)); return false; } return true; }); if (!ok) { return ok.takeError(); } if (!result) { return make_error(name); } return std::move(*result); } Result Union::field(uint32_t index) const { std::optional result; auto ok = forEachField( handle(), btf_type(), [&]([[maybe_unused]] std::string &&field_name, FieldInfo &&info) -> bool { result.emplace(std::move(info)); return true; }, index, index + 1); if (!ok) { return ok.takeError(); } if (!result) { return make_error(index); } return std::move(*result); } Result>> Union::fields() const { std::vector> result; auto ok = forEachField( handle(), btf_type(), [&](std::string &&field_name, FieldInfo &&info) -> bool { result.emplace_back(std::move(field_name), std::move(info)); return true; }); if (!ok) { return ok.takeError(); } return result; } std::string Enum::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } std::map Enum::values() const { std::map values; const auto *t = btf_type(); auto *v = btf_enum(t); auto vlen = btf_vlen(t); for (int i = 0; i < vlen; i++) { std::string s(btf__name_by_offset(btf_library(), v[i].name_off)); values.emplace(std::move(s), static_cast(v[i].val)); } return values; } Result Enum::add(HandleRef handle, const std::string &name, const std::map &values) { auto v = btf__add_enum(handle->btf_library(), name.c_str(), sizeof(int32_t)); if (v < 0) { return make_error(v); } for (const auto &[value_name, v] : values) { btf__add_enum_value(handle->btf_library(), value_name.c_str(), v); } return Enum(std::move(handle), v); } std::string Enum64::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } std::map Enum64::values() const { std::map values; const auto *t = btf_type(); auto *v = btf_enum64(t); auto vlen = btf_vlen(t); for (int i = 0; i < vlen; i++) { std::string s(btf__name_by_offset(btf_library(), v[i].name_off)); values.emplace(std::move(s), (static_cast(v[i].val_hi32) << 32) | static_cast(v[i].val_lo32)); } return values; } Result Enum64::add(HandleRef handle, const std::string &name, const std::map &values) { auto v = btf__add_enum64( handle->btf_library(), name.c_str(), sizeof(int64_t), true); if (v < 0) { return make_error(v); } for (const auto &[value_name, v] : values) { btf__add_enum64_value(handle->btf_library(), value_name.c_str(), v); } return Enum64(std::move(handle), v); } ForwardDecl::Kind ForwardDecl::kind() const { // Note that enums don't really exist in the encoded scheme, they are just // enums without any defined values. bool is_union = BTF_INFO_KFLAG(btf_type()->info); if (is_union) { return ForwardDecl::Kind::Union; } return ForwardDecl::Kind::Struct; } std::string ForwardDecl::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } Result Typedef::add(HandleRef handle, const std::string &name, const AnyType &t) { auto v = btf__add_typedef(handle->btf_library(), name.c_str(), t.type_id()); if (v < 0) { return make_error(v); } return Typedef(std::move(handle), v); } std::string Typedef::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } Result Typedef::type() const { return AnyType::lookup(handle(), btf_type()->type); } Result Volatile::add(HandleRef handle, const ValueType &value_type) { auto v = btf__add_volatile(handle->btf_library(), value_type.type_id()); if (v < 0) { return make_error(v); } return Volatile(std::move(handle), v); } Result Volatile::type() const { return AnyType::lookup(handle(), btf_type()->type); } Result Const::add(HandleRef handle, const ValueType &value_type) { auto v = btf__add_const(handle->btf_library(), value_type.type_id()); if (v < 0) { return make_error(v); } return Const(std::move(handle), v); } Result Const::type() const { return AnyType::lookup(handle(), btf_type()->type); } Result Restrict::add(HandleRef handle, const ValueType &value_type) { auto v = btf__add_restrict(handle->btf_library(), value_type.type_id()); if (v < 0) { return make_error(v); } return Restrict(std::move(handle), v); } Result Restrict::type() const { return AnyType::lookup(handle(), btf_type()->type); } Result FunctionProto::add( HandleRef handle, const ValueType &return_type, const std::vector> &args) { auto v = btf__add_func_proto(handle->btf_library(), return_type.type_id()); if (v < 0) { return make_error(v); } for (const auto &[param_name, t] : args) { btf__add_func_param(handle->btf_library(), param_name.c_str(), t.type_id()); } return FunctionProto(std::move(handle), v); } Result>> FunctionProto:: argument_types() const { std::vector> result; const auto *t = btf_type(); auto *params = btf_params(t); auto vlen = btf_vlen(t); for (int i = 0; i < vlen; i++) { auto v = ValueType::lookup(handle(), params[i].type); if (!v) { return v.takeError(); } result.emplace_back(std::string(btf__name_by_offset(btf_library(), params[i].name_off)), std::move(*v)); } return result; } Result FunctionProto::return_type() const { return ValueType::lookup(handle(), btf_type()->type); } Result Function::add(HandleRef handle, const std::string &name, Linkage linkage, const FunctionProto &proto) { auto v = btf__add_func(handle->btf_library(), name.c_str(), static_cast(linkage), proto.type_id()); if (v < 0) { return make_error(v); } return Function(std::move(handle), v); } Result Function::type() const { return FunctionProto::lookup(handle(), btf_type()->type); } Function::Linkage Function::linkage() const { return static_cast(btf_vlen(btf_type())); } std::string Function::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } Result Var::add(HandleRef handle, const std::string &name, Linkage linkage, const ValueType &type) { auto v = btf__add_var( handle->btf_library(), name.c_str(), linkage, type.type_id()); if (v < 0) { return make_error(v); } return Var(std::move(handle), v); } Result Var::type() const { return ValueType::lookup(handle(), btf_type()->type); } Var::Linkage Var::linkage() const { auto *var = btf_var(btf_type()); return static_cast(var->linkage); } std::string Var::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } std::string DataSection::name() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } Result DataSection::add(HandleRef handle, const std::string &name, const std::vector &vars) { size_t total_bytes = 0; std::vector sizes; std::vector offsets; for (const auto &var : vars) { auto align = var.alignment(); if (!align) { return align.takeError(); } auto left = total_bytes % (*align); if (left != 0) { total_bytes += (*align) - left; } offsets.push_back(total_bytes); auto size = var.size(); if (!size) { return size.takeError(); } sizes.push_back(*size); total_bytes += (*size); } auto v = btf__add_datasec(handle->btf_library(), name.c_str(), total_bytes); if (v < 0) { return make_error(v); } for (size_t i = 0; i < vars.size(); i++) { auto r = btf__add_datasec_var_info( handle->btf_library(), vars[i].type_id(), offsets[i], sizes[i]); if (r < 0) { return make_error(r); } } return DataSection(std::move(handle), v); } std::string DeclTag::value() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } std::string TypeTag::value() const { return btf__name_by_offset(btf_library(), btf_type()->name_off); } Result TypeTag::element_type() const { return AnyType::lookup(handle(), btf_type()->type); } Result Types::parse(const void *data, size_t sz) { auto handle = detail::Handle::parse(data, sz); if (!handle) { return handle.takeError(); } return Types(std::move(*handle)); } Result Types::parse(const Types &base, const void *data, size_t sz) { auto handle = detail::Handle::parse(base.handle_, data, sz); if (!handle) { return handle.takeError(); } return Types(std::move(*handle)); } size_t Types::size() const { return btf__type_cnt(handle_->btf_library()); } Result Types::distill() { struct btf *new_base_btf = nullptr; struct btf *new_split_btf = nullptr; int rc = btf__distill_base(handle_->btf_library(), &new_base_btf, &new_split_btf); if (rc < 0) { return make_error(rc); } // Replace the current one with the new base, and return // the new top-level target. handle_ = std::make_shared(new_base_btf); return Types(std::make_shared(new_split_btf, handle_)); } Result Types::relocate(const Types &other) { int rc = btf__relocate(handle_->btf_library(), other.handle_->btf_library()); if (rc < 0) { return make_error(rc); } handle_->rebase(other.handle_); return OK(); } Result Types::append(const Types &other) { int rc = btf__add_btf(handle_->btf_library(), other.handle_->btf_library()); if (rc < 0) { return make_error(rc); } return OK(); } static void dump_printf(void *ctx, const char *fmt, va_list args) { auto &out = *static_cast(ctx); char *str; if (vasprintf(&str, fmt, args) < 0) return; out << str; free(str); } Result<> Types::emit_decl(std::ostream &out) const { struct btf_dump *dump = btf_dump__new( handle_->btf_library(), dump_printf, static_cast(&out), nullptr); if (!dump) { return make_error(errno); } size_t sz = size(); for (size_t i = 1; i < sz; i++) { int rc = btf_dump__dump_type(dump, i); if (rc) { return make_error(rc); } } return OK(); } std::ostream &operator<<(std::ostream &out, const BaseType &type) { auto name_offset = type.btf_type()->name_off; if (name_offset != 0) { out << btf__name_by_offset(type.btf_library(), name_offset); } else { out << "(anon)"; } return out; } std::ostream &operator<<(std::ostream &out, [[maybe_unused]] const Void &type) { out << "void"; return out; } std::ostream &operator<<(std::ostream &out, const Integer &type) { out << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const Pointer &type) { auto t = type.element_type(); if (!t) { out << "(error: " << t.takeError() << ")"; } else { out << *t; } out << "*"; return out; } std::ostream &operator<<(std::ostream &out, const Array &type) { auto t = type.element_type(); if (!t) { llvm::Error err = t.takeError(); out << "(error: " << err << ")"; } else { out << *t; } out << "[" << type.element_count() << "]"; return out; } std::ostream &operator<<(std::ostream &out, const Struct &type) { auto info = getMapInfo(type); if (!info) { out << "struct " << static_cast(type); } else { // This is a map we can print it special. out << "map{type=" << static_cast(info->map_type) << ", key_type=" << info->key << ", value_type=" << info->value << ", nr_elements=" << info->nr_elements << "}"; } return out; } std::ostream &operator<<(std::ostream &out, const Union &type) { out << "union " << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const Enum &type) { out << "enum " << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const Enum64 &type) { out << "enum64 " << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const ForwardDecl &type) { out << "enum " << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const Typedef &type) { out << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const Volatile &type) { auto t = type.type(); out << "volatile "; if (!t) { out << "(error: " << t.takeError() << ")"; } else { out << *t; } return out; } std::ostream &operator<<(std::ostream &out, const Const &type) { auto t = type.type(); out << "const "; if (!t) { out << "(error: " << t.takeError() << ")"; } else { out << *t; } return out; } std::ostream &operator<<(std::ostream &out, const Restrict &type) { auto t = type.type(); out << "restrict "; if (!t) { out << "(error: " << t.takeError() << ")"; } else { out << *t; } return out; } static void formatFunction(std::ostream &out, const std::string &name, const FunctionProto &type) { auto rt = type.return_type(); if (!rt) { out << "(error: " << rt.takeError() << ") "; } else { out << *rt << " "; } out << name; auto args = type.argument_types(); if (!args) { out << "(error: " << args.takeError() << ")"; } else { out << "("; bool first = true; for (const auto &[name, t] : *args) { if (!first) { out << ", "; } out << t; if (!name.empty()) { out << " " << name; } first = false; } out << ")"; } } std::ostream &operator<<(std::ostream &out, const FunctionProto &type) { formatFunction(out, "(*)", type); return out; } std::ostream &operator<<(std::ostream &out, const Function &type) { std::string name = type.name(); auto t = type.type(); if (!t) { out << "void " << name << "(error: " << t.takeError() << ")"; } else { formatFunction(out, name, *t); } return out; } std::ostream &operator<<(std::ostream &out, const Var &type) { auto t = type.type(); if (!t) { out << "(error: " << t.takeError() << ") "; } else { out << *t << " "; } out << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const DataSection &type) { out << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const Float &type) { out << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const DeclTag &type) { out << static_cast(type); return out; } std::ostream &operator<<(std::ostream &out, const TypeTag &type) { out << static_cast(type); return out; } } // namespace bpftrace::btf bpftrace-0.24.1/src/btf/btf.h000066400000000000000000000615311506776124200156720ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include "util/result.h" namespace bpftrace::btf { namespace detail { class Handle; using HandleRef = std::shared_ptr; // Handle is the internal library BTF handle. This is shared among all objects // that may be referencing the `struct btf*` pointer. It is held as a // `shared_ptr`, using the alias `HandleRef` (defined above). class Handle { public: Handle(); Handle(HandleRef base); Handle(struct btf *btf) : btf_(btf) {}; Handle(struct btf *btf, HandleRef base) : btf_(btf), base_(std::move(base)) {}; ~Handle(); Handle(Handle &&other) = delete; Handle &operator=(Handle &&other) = delete; Handle(const Handle &other) = delete; Handle &operator=(Handle &other) = delete; static Result parse(const void *data, size_t sz); static Result parse(HandleRef base, const void *data, size_t sz); struct btf *btf_library() const { return btf_; } void rebase(HandleRef base) { base_ = std::move(base); } private: struct btf *btf_; // N.B. base_ is not used directly, but holds a reference to the btf_library // handle if this is needed. This ensures it is not released while it is being // used. It is only used indirectly through our own handle above. HandleRef base_; }; } // namespace detail using detail::HandleRef; // Indicates an error occured while parsing BTF. class ParseError : public ErrorInfo { public: static char ID; ParseError(int err) : err_(err >= 0 ? err : -err) {}; void log(llvm::raw_ostream &OS) const override; private: int err_; }; // Indicates an error occurred within libbtf. class TypeError : public ErrorInfo { public: static char ID; TypeError(int err) : err_(err >= 0 ? err : -err) {}; void log(llvm::raw_ostream &OS) const override; private: int err_; }; // Indicates that no associated type was found, either when looking up by name // or by type_id. class UnknownType : public ErrorInfo { public: static char ID; UnknownType(const std::string &name) : name_(name) {}; UnknownType(uint32_t type_id) : name_(type_id) {}; void log(llvm::raw_ostream &OS) const override; private: std::variant name_; }; // Indicates that a type was found, but it did not match the expected type. class KindMismatch : public ErrorInfo { public: static char ID; KindMismatch(uint32_t got, uint32_t expected) : got_(got), expected_(expected) {}; void log(llvm::raw_ostream &OS) const override; private: uint32_t got_; uint32_t expected_; }; class Void; class Integer; class Pointer; class Array; class Struct; class Union; class Enum; class Enum64; class ForwardDecl; class Typedef; class Volatile; class Const; class Restrict; class FunctionProto; class Function; class Var; class DataSection; class Float; class DeclTag; class TypeTag; // Defined below for ordering. template class VariantType; // ValueType represents a value that may be passed to functions, returned, etc. // It excludes special types that may exist only within BTF (e.g. function, // variable, data section, tags, etc.). using ValueType = VariantType; // AnyType is the set of all possible BTF types. using AnyType = VariantType; // BaseType is the concrete implementation of the abstract `Type`. class BaseType { public: // Construct a new object with the handle and type_id. This is the universal // constructor for all types; it should not be overriden. BaseType(HandleRef handle, uint32_t type_id) : handle_(std::move(handle)), type_id_(type_id) {}; // Return the resolved size of the type. Result size() const; // Returns the required alignment for the type. Result alignment() const; // Return the handle for the type. const struct btf_type *btf_type() const; // Returns the library handle. HandleRef handle() const { return handle_; } // Returns the type_id. uint32_t type_id() const { return type_id_; } // Returns the btf library data, convenience wrapper. struct btf *btf_library() const { return handle_->btf_library(); } private: HandleRef handle_; uint32_t type_id_; }; // Type is the common implementation for all types; this class uses the CRTP // pattern to eliminate as much boilerplate as possible. // // Each subclass should define the following: // - An `add` method, which will lookup if the type already exists, and if not // add to the handle and return the result. // - Any custom methods related to extracting type-specfic information. template class Type : public BaseType { public: using BaseType::BaseType; // Used by the variant above, to determine if this accepts the given kind. static bool is(uint32_t kind) { return kind == Kind; } // Finds the type with the required kind and name, constructing a new object. static Result lookup(HandleRef handle, const std::string &name) { auto v = btf__find_by_name_kind(handle->btf_library(), name.c_str(), Kind); if (v >= 0) { return T(std::move(handle), v); } return make_error(name); } // Checks the given type_id against the passed one. static Result lookup(HandleRef handle, uint32_t type_id) { const auto *t = btf__type_by_id(handle->btf_library(), type_id); if (t == nullptr) { return make_error(type_id); } auto k = btf_kind(t); if (k != Kind) { return make_error(k, Kind); } return T(std::move(handle), type_id); } friend class Types; friend std::ostream &operator<<(std::ostream &, const BaseType &); }; class Void : public Type { public: Void(HandleRef handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; static Result lookup(HandleRef handle, const std::string &name) { if (name == "void") { return Void(std::move(handle), 0); } return make_error(name); } static Result lookup(HandleRef handle, uint32_t type_id) { if (type_id == 0) { return Void(std::move(handle), type_id); } return make_error(type_id); } private: // There is no `add` method for void types, it is always implicitly defined. // It is also used for types that we cannot currently interpret correctly. friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Void & /*type*/); }; class Integer : public Type { public: Integer(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; size_t bytes() const; private: static Result add(HandleRef handle, const std::string &name, size_t bytes, int encoding); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Integer & /*type*/); }; class Pointer : public Type { public: Pointer(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; Result element_type() const; private: static Result add(HandleRef handle, const AnyType &type); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Pointer & /*type*/); }; class Array : public Type { public: Array(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; Result index_type() const; Result element_type() const; size_t element_count() const; private: static Result add(HandleRef handle, const Integer &index_type, const ValueType &element_type, size_t elements); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Array & /*type*/); }; // Defined below. struct FieldInfo; class Struct : public Type { public: Struct(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string name() const; Result field(const std::string &name) const; Result field(uint32_t index) const; Result>> fields() const; private: static Result add( HandleRef handle, const std::string &name, const std::vector> &fields); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Struct & /*type*/); }; class Union : public Type { public: Union(HandleRef handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string name() const; Result field(const std::string &name) const; Result field(uint32_t index) const; Result>> fields() const; private: static Result add( HandleRef handle, const std::string &name, const std::vector> &fields); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Union & /*type*/); }; class Enum : public Type { public: Enum(HandleRef handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string name() const; std::map values() const; private: static Result add(HandleRef handle, const std::string &name, const std::map &values); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Enum & /*type*/); }; class Enum64 : public Type { public: Enum64(HandleRef handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string name() const; std::map values() const; private: static Result add(HandleRef handle, const std::string &name, const std::map &values); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Enum64 & /*type*/); }; class ForwardDecl : public Type { public: ForwardDecl(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; enum Kind { Struct = BTF_FWD_STRUCT, Union = BTF_FWD_UNION, Enum = BTF_FWD_ENUM, }; Kind kind() const; std::string name() const; private: // We don't support `add` for forward declarations, this is really an // artifact used for constructing pointers without full type information in // all translation units. It should not be relevant in fully fleshed out // types, hence the only method is the resolution method. friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const ForwardDecl & /*type*/); }; class Typedef : public Type { public: Typedef(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string name() const; Result type() const; private: static Result add(HandleRef handle, const std::string &name, const AnyType &t); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Typedef & /*type*/); }; class Volatile : public Type { public: Volatile(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; Result type() const; private: static Result add(HandleRef handle, const ValueType &value_type); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Volatile & /*type*/); }; class Const : public Type { public: Const(HandleRef handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; Result type() const; private: static Result add(HandleRef handle, const ValueType &value_type); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Const & /*type*/); }; class Restrict : public Type { public: Restrict(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; Result type() const; private: static Result add(HandleRef handle, const ValueType &value_type); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Restrict & /*type*/); }; class FunctionProto : public Type { public: FunctionProto(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; Result>> argument_types() const; Result return_type() const; private: static Result add( HandleRef handle, const ValueType &return_type, const std::vector> &args); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const FunctionProto & /*type*/); }; class Function : public Type { public: Function(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; enum Linkage { Static = BTF_FUNC_STATIC, Global = BTF_FUNC_GLOBAL, Extern = BTF_FUNC_EXTERN, }; Result type() const; Linkage linkage() const; std::string name() const; private: static Result add(HandleRef handle, const std::string &name, Linkage linkage, const FunctionProto &proto); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Function & /*type*/); }; class Var : public Type { public: Var(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; enum Linkage { Static = BTF_VAR_STATIC, Global = BTF_VAR_GLOBAL_ALLOCATED, Extern = BTF_VAR_GLOBAL_EXTERN, }; Result type() const; Linkage linkage() const; std::string name() const; private: static Result add(HandleRef handle, const std::string &name, Linkage linkage, const ValueType &type); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Var & /*type*/); }; class DataSection : public Type { public: DataSection(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string name() const; private: static Result add(HandleRef handle, const std::string &name, const std::vector &vars); friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const DataSection & /*type*/); }; class Float : public Type { public: Float(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; private: // We don't presently support floating point operations, and hence do not // have the ability to add anything to this type system. friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const Float & /*type*/); }; class DeclTag : public Type { public: DeclTag(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string value() const; private: friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const DeclTag & /*type*/); }; class TypeTag : public Type { public: TypeTag(HandleRef &&handle, uint32_t type_id) : Type(std::move(handle), type_id) {}; std::string value() const; Result element_type() const; private: friend class Types; friend std::ostream &operator<<(std::ostream & /*out*/, const TypeTag & /*type*/); }; // VariantType represents a variant over some set of possible types. template class VariantType { public: VariantType(std::variant value) : value_(std::move(value)) {}; // Allow automatic construct from any supported type. template VariantType(const U &value) requires((std::is_same_v || ...)) : value_(value){}; // Allow conversion between types as long as one is strictly a subset of the // other. In general, this allows `ValueType` to `AnyType` implicit // conversion. template VariantType(VariantType other) : VariantType( std::visit([](const auto &v) -> std::variant { return v; }, other.value())){}; // Checks to see if this is the given kind. template bool is() const { return std::holds_alternative(value_); } // Interpret as the given kind; use `is` first. template const T &as() const { return std::get(value_); } // Return the size of the type. Result size() const { return std::visit([](const auto &v) { return v.size(); }, value_); } // Returns the alignment of the type. Result alignment() const { return std::visit([](const auto &v) { return v.alignment(); }, value_); } // Return the underlying type_id for the type. uint32_t type_id() const { return std::visit([](const auto &v) { return v.type_id(); }, value_); } static Result> lookup(HandleRef handle, uint32_t type_id) { const auto *t = btf__type_by_id(handle->btf_library(), type_id); assert(t != nullptr); auto k = btf_kind(t); return tryLookup(k, std::move(handle), type_id); } static Result> lookup(HandleRef handle, const std::string &name) { return tryLookup(std::move(handle), name); } const std::variant &value() const { return value_; } private: std::variant value_; template static Result> tryLookup(uint32_t kind, HandleRef handle, uint32_t type_id) { if (First::is(kind)) { // This matches the kind provided, so we delegate to this class. Note // that this may still return an error, and we propagate that explicitly. auto ok = First::lookup(std::move(handle), type_id); if (!ok) { return ok.takeError(); } return VariantType(std::move(*ok)); } if constexpr (sizeof...(Rest) != 0) { // Keep tring the rest of the underlying types. return tryLookup(kind, std::move(handle), type_id); } return make_error(type_id); } template static Result> tryLookup(HandleRef handle, const std::string &name) { auto ok = First::lookup(handle, name); if (ok) { return std::move(*ok); } if constexpr (sizeof...(Rest) != 0) { return tryLookup(std::move(handle), name); } return make_error(name); } friend class Types; template friend std::ostream &operator<<(std::ostream &, const VariantType &); }; template std::ostream &operator<<(std::ostream &out, const VariantType &type) { std::visit([&](const auto &v) { out << v; }, type.value_); return out; } // Information for fields. struct FieldInfo { ValueType type; size_t bit_offset; size_t bit_size; }; namespace detail { // Basic iterator over times, used to provide a view into the system. class TypeIterator { public: using value_type = AnyType; using difference_type = std::ptrdiff_t; class Sentinel { public: uint32_t limit; }; // Used for `end`. TypeIterator(HandleRef handle, uint32_t type_id) : handle_(std::move(handle)), type_id_(type_id) {}; AnyType operator*() const { auto ok = AnyType::lookup(handle_, type_id_); if (!ok) { // We expect this to happen only if the iterator is being used // incorrectly. If exceptions are disable in this case, then everything // will merely die. This seems like the best outcome. std::stringstream ss; ss << ok.takeError(); throw std::out_of_range(ss.str()); } return std::move(*ok); } TypeIterator &operator++() { type_id_++; return *this; } TypeIterator operator++(int) { TypeIterator temp(handle_, type_id_); ++(*this); return temp; } bool operator==(const TypeIterator &other) const { return handle_ == other.handle_ && type_id_ == other.type_id_; } bool operator==(const Sentinel &other) const { return type_id_ >= other.limit; } private: HandleRef handle_; uint32_t type_id_; }; } // namespace detail // Types represents a self-contained type system. It is typically constructed // from a serialized BTF dataset, but may be constructed dynamically. class Types { public: Types() : handle_(std::make_shared()) {}; Types(const Types &base) : handle_(std::make_shared(base.handle_)) {}; Types(Types &&other) = default; Types &operator=(Types &&other) = default; static Result parse(const void *data, size_t sz); static Result parse(const Types &base, const void *data, size_t sz); // Iterators. detail::TypeIterator begin() { return { handle_, 0 }; } detail::TypeIterator::Sentinel end() { return { .limit = static_cast(size()) }; } // Returns the number of types. size_t size() const; // See `bpf__distill_base`. Result distill(); // Switches the base of this system to another. Result relocate(const Types &other); // Append another type system to this one. Result append(const Types &other); // Add adds the given type. template Result add(Args... args) { return T::add(handle_, args...); } // Lookup will lookup the given type by the type string. template Result lookup(const std::string &name) const { return T::lookup(handle_, name); } // ... or, we can lookup by the type_id. template Result lookup(uint32_t type_id) const { return T::lookup(handle_, type_id); } // Dumps a stream of C declarations. Result<> emit_decl(std::ostream &out) const; private: Types(HandleRef &&handle) : handle_(std::move(handle)) {}; HandleRef handle_; }; } // namespace bpftrace::btf bpftrace-0.24.1/src/btf/compat.cpp000066400000000000000000000045001506776124200167260ustar00rootroot00000000000000#include "btf/compat.h" #include "struct.h" namespace bpftrace::btf { char CompatTypeError::ID; void CompatTypeError::log(llvm::raw_ostream &OS) const { OS << msg_; } Result getCompatType([[maybe_unused]] const Void &type) { return CreateVoid(); } Result getCompatType(const Integer &type) { return CreateInt(8 * type.bytes()); } Result getCompatType(const Pointer &type) { auto t = type.element_type(); if (!t) { return t.takeError(); } auto ty = getCompatType(*t); if (!ty) { return ty.takeError(); } return CreatePointer(*ty); } Result getCompatType(const Array &type) { auto t = type.element_type(); if (!t) { return t.takeError(); } auto ty = getCompatType(*t); if (!ty) { return ty.takeError(); } return CreateArray(type.element_count(), *ty); } Result getCompatType(const Struct &type) { std::vector fields; std::vector idents; auto f = type.fields(); if (!f) { return f.takeError(); } // We have no ability to control how things are packed, // so this may or may not generate the correct type. for (const auto &[name, info] : *f) { auto ft = getCompatType(info.type); if (!ft) { return ft.takeError(); } fields.emplace_back(*ft); idents.emplace_back(name); } return CreateRecord(bpftrace::Struct::CreateRecord(fields, idents)); } Result getCompatType(const Enum &type) { auto size = type.size(); if (!size) { return size.takeError(); } return CreateEnum(8 * (*size), type.name()); } Result getCompatType(const Enum64 &type) { auto size = type.size(); if (!size) { return size.takeError(); } return CreateEnum(8 * (*size), type.name()); } Result getCompatType(const TypeTag &type) { // Values are: user, percpu, rcu. if (type.value() == "user") { auto t = type.element_type(); if (!t) { return t.takeError(); } auto ty = getCompatType(*t); if (!ty) { return ty.takeError(); } ty->SetAS(AddrSpace::user); return *ty; } else { return make_error(type); } } Result getCompatType(const Typedef &type) { auto t = type.type(); if (!t) { return t.takeError(); } return getCompatType(*t); } } // namespace bpftrace::btf bpftrace-0.24.1/src/btf/compat.h000066400000000000000000000027451506776124200164040ustar00rootroot00000000000000#pragma once #include "btf/btf.h" #include "types.h" #include "util/result.h" namespace bpftrace::btf { class CompatTypeError : public ErrorInfo { public: template CompatTypeError(const T &type) { std::stringstream ss; ss << "no compatible type for " << type; msg_ = ss.str(); } static char ID; void log(llvm::raw_ostream &OS) const override; const std::string &msg() const { return msg_; } private: std::string msg_; }; template struct compatTypeFor { Result operator()(const T &t) { return make_error(t); } }; template struct compatTypeFor> { Result operator()(const VariantType &type) { return std::visit([&](const auto &v) { return getCompatType(v); }, type.value()); } }; Result getCompatType(const Void &type); Result getCompatType(const Integer &type); Result getCompatType(const Pointer &type); Result getCompatType(const Array &type); Result getCompatType(const Struct &type); Result getCompatType(const Enum &type); Result getCompatType(const Enum64 &type); Result getCompatType(const TypeTag &type); Result getCompatType(const Typedef &type); template Result getCompatType(const T &type) { compatTypeFor op; return op(type); } } // namespace bpftrace::btf bpftrace-0.24.1/src/btf/helpers.cpp000066400000000000000000000106771506776124200171210ustar00rootroot00000000000000#include "btf/helpers.h" namespace bpftrace::btf { char MapError::ID; void MapError::log(llvm::raw_ostream &OS) const { OS << msg_; } Result createTuple(Types &btf, const std::vector &types) { std::vector> fields; for (const auto &type : types) { fields.emplace_back("", type); } return btf.add("", fields); } Result createMap(Types &btf, const struct MapInfo &info) { std::vector> fields; // First, we need an "int" type. auto i = btf.lookup("int"); if (!i) { i = btf.add("int", sizeof(int), true); if (!i) { return i.takeError(); } } // Now we need an `__ARRAY_SIZE_TYPE__`. auto sz = btf.lookup("__ARRAY_SIZE_TYPE__"); if (!sz) { sz = btf.add("__ARRAY_SIZE_TYPE__", sizeof(int), true); if (!sz) { return sz.takeError(); } } // Now we need to encode the type and max_entries. auto enc_type_arr = btf.add(*sz, *i, static_cast(info.map_type)); if (!enc_type_arr) { return enc_type_arr.takeError(); } auto enc_type_ptr = btf.add(*enc_type_arr); if (!enc_type_ptr) { return enc_type_ptr.takeError(); } auto enc_max_entries_arr = btf.add(*sz, *i, info.nr_elements); if (!enc_max_entries_arr) { return enc_max_entries_arr.takeError(); } auto enc_max_entries_ptr = btf.add(*enc_max_entries_arr); if (!enc_max_entries_ptr) { return enc_max_entries_ptr.takeError(); } auto enc_key = btf.add(info.key); if (!enc_key) { return enc_key.takeError(); } auto enc_value = btf.add(info.value); if (!enc_value) { return enc_value.takeError(); } fields.emplace_back("type", *enc_type_ptr); fields.emplace_back("max_entries", *enc_max_entries_ptr); fields.emplace_back("key", *enc_key); fields.emplace_back("value", *enc_value); auto r = btf.add("", fields); if (!r) { return r.takeError(); } return std::move(*r); } Result getMapInfo(const AnyType &type) { if (!type.is()) { return make_error("not a struct"); } const auto &s = type.as(); auto fields = s.fields(); if (!fields) { return fields.takeError(); } // Validate that all fields are present and in order. if (fields->size() != 4) { return make_error("does not have four fields"); } static const std::vector expected = { "type", "max_entries", "key", "value" }; size_t elem = 0; for (const auto &[name, _] : *fields) { if (name != expected[elem]) { return make_error("field names do not match"); } elem++; } // Extract the type. auto &type_info = fields->at(0).second; ::bpf_map_type map_type = BPF_MAP_TYPE_ARRAY; if (!type_info.type.is()) { return make_error("type field is not a pointer"); } else { auto arr = type_info.type.as().element_type(); if (!arr || !arr->is()) { return make_error("type field is not a pointer to an array"); } map_type = static_cast<::bpf_map_type>(arr->as().element_count()); } // Extract the number of elements. auto &max_entries = fields->at(1).second; size_t nr_elements = 0; if (!max_entries.type.is()) { return make_error("max_entries field is not a pointer"); } else { auto arr = max_entries.type.as().element_type(); if (!arr || !arr->is()) { return make_error( "max_entries field is not a pointer to an array"); } nr_elements = arr->as().element_count(); } // Extract the key and value types. auto &key_ptr = fields->at(2).second.type; if (!key_ptr.is()) { return make_error("key field is not a pointer"); } auto key_type = key_ptr.as().element_type(); if (!key_type) { return key_type.takeError(); } auto &value_ptr = fields->at(3).second.type; if (!value_ptr.is()) { return make_error("value field is not a pointer"); } auto value_type = value_ptr.as().element_type(); if (!value_type) { return value_type.takeError(); } struct MapInfo info = { .map_type = map_type, .key = *key_type, .value = *value_type, .nr_elements = nr_elements, }; return info; } } // namespace bpftrace::btf bpftrace-0.24.1/src/btf/helpers.h000066400000000000000000000015051506776124200165540ustar00rootroot00000000000000#pragma once #include "btf/btf.h" #include "libbpf/bpf.h" namespace bpftrace::btf { // Indicates an error occured while parsing BTF. class MapError : public ErrorInfo { public: static char ID; MapError(std::string msg) : msg_(std::move(msg)) {}; void log(llvm::raw_ostream &OS) const override; private: std::string msg_; }; // A tuple is an anonymous struct. Result createTuple(Types &btf, const std::vector &types); // Defines a map. struct MapInfo { ::bpf_map_type map_type; AnyType key; AnyType value; size_t nr_elements; }; // A map as the standard specialized Types definition. Result createMap(Types &btf, const struct MapInfo &info); // Returns the information for the map, if it is a map. Result getMapInfo(const AnyType &type); } // namespace bpftrace::btf bpftrace-0.24.1/src/btf/irbuilder.cpp000066400000000000000000000100111506776124200174160ustar00rootroot00000000000000#include "btf/irbuilder.h" namespace bpftrace::btf { static Result intType(llvm::IRBuilderBase &base, size_t bits) { switch (bits) { case 1: return base.getInt1Ty(); case 8: return base.getInt8Ty(); case 16: return base.getInt16Ty(); case 32: return base.getInt32Ty(); case 64: return base.getInt64Ty(); default: return make_error(EINVAL); } } Result getType(llvm::IRBuilderBase &base, const Integer &type) { return intType(base, 8 * type.bytes()); } Result getType(llvm::IRBuilderBase &base, [[maybe_unused]] const Pointer &type) { return base.getPtrTy(); } Result getType(llvm::IRBuilderBase &base, const Array &type) { auto t = type.element_type(); if (!t) { return t.takeError(); } auto ty = getType(base, t); if (!ty) { return ty.takeError(); } return llvm::ArrayType::get(*ty, type.element_count()); } Result getType(llvm::IRBuilderBase &base, const Struct &type) { llvm::SmallVector fields; auto f = type.fields(); if (!f) { return f.takeError(); } size_t last_offset = 0; bool packed = true; for (const auto &[name, info] : *f) { if (info.bit_offset != last_offset) { packed = false; } if (info.bit_size != 0) { last_offset += info.bit_size; } else { auto size = info.type.size(); if (!size) { return size.takeError(); } last_offset += 8 * (*size); } auto ft = getType(base, info.type); if (!ft) { return ft.takeError(); } fields.push_back(*ft); } return llvm::StructType::create( base.getContext(), fields, type.name(), packed); } Result getType(llvm::IRBuilderBase &base, const Union &type) { // There are no unions in LLVM IR. Therefore, we need to construct a // struct with appropriate size. auto size = type.size(); if (!size) { return size.takeError(); } auto *bytes = llvm::ArrayType::get(base.getInt8Ty(), *size); return llvm::StructType::create( base.getContext(), { bytes }, type.name(), true); } Result getType(llvm::IRBuilderBase &base, const Enum &type) { auto size = type.size(); if (!size) { return size.takeError(); } return intType(base, 8 * (*size)); } Result getType(llvm::IRBuilderBase &base, const Enum64 &type) { auto size = type.size(); if (!size) { return size.takeError(); } return intType(base, 8 * (*size)); } Result getType(llvm::IRBuilderBase &base, const Typedef &type) { auto t = type.type(); if (!t) { return t.takeError(); } return getType(base, *t); } Result getType(llvm::IRBuilderBase &base, const Volatile &type) { auto t = type.type(); if (!t) { return t.takeError(); } return getType(base, *t); } Result getType(llvm::IRBuilderBase &base, const Const &type) { auto t = type.type(); if (!t) { return t.takeError(); } return getType(base, *t); } Result getType(llvm::IRBuilderBase &base, const Restrict &type) { auto t = type.type(); if (!t) { return t.takeError(); } return getType(base, *t); } Result getType(llvm::IRBuilderBase &base, const FunctionProto &type) { llvm::SmallVector params; auto rt = type.return_type(); if (!rt) { return rt.takeError(); } auto rtt = getType(base, rt); if (!rtt) { return rtt.takeError(); } auto args = type.argument_types(); if (!args) { return args.takeError(); } bool var_args = false; size_t count = 0; for (const auto &[_, arg] : *args) { if (count == args->size() - 1 && arg.is()) { // This is a vararg function. var_args = true; break; } auto t = getType(base, arg); if (!t) { return t.takeError(); } params.push_back(*t); count++; } return llvm::FunctionType::get(*rtt, params, var_args); } } // namespace bpftrace::btf bpftrace-0.24.1/src/btf/irbuilder.h000066400000000000000000000033501506776124200170730ustar00rootroot00000000000000#pragma once #include #include "btf/btf.h" namespace bpftrace::btf { template struct defaultIRTypeFor { Result operator()(llvm::IRBuilderBase &base, [[maybe_unused]] const T &t) { return base.getVoidTy(); } }; template struct defaultIRTypeFor> { Result operator()(llvm::IRBuilderBase &base, const VariantType &type) { return std::visit([&](const auto &v) { return getType(base, v); }, type.value()); } }; Result getType(llvm::IRBuilderBase &base, const Integer &type); Result getType(llvm::IRBuilderBase &base, const Pointer &type); Result getType(llvm::IRBuilderBase &base, const Array &type); Result getType(llvm::IRBuilderBase &base, const Struct &type); Result getType(llvm::IRBuilderBase &base, const Union &type); Result getType(llvm::IRBuilderBase &base, const Enum &type); Result getType(llvm::IRBuilderBase &base, const Enum64 &type); Result getType(llvm::IRBuilderBase &base, const Typedef &type); Result getType(llvm::IRBuilderBase &base, const Volatile &type); Result getType(llvm::IRBuilderBase &base, const Const &type); Result getType(llvm::IRBuilderBase &base, const Restrict &type); Result getType(llvm::IRBuilderBase &base, const FunctionProto &type); template Result getType(llvm::IRBuilderBase &base, const T &type) { defaultIRTypeFor op; return op(base, type); } } // namespace bpftrace::btf bpftrace-0.24.1/src/build_info.cpp000066400000000000000000000017001506776124200170010ustar00rootroot00000000000000#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 << " libdw (DWARF support): " #ifdef HAVE_LIBDW << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " libsystemd (systemd notify support): " #ifdef HAVE_LIBSYSTEMD << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " blazesym (advanced symbolization): " #ifdef HAVE_BLAZESYM << "yes" << std::endl; #else << "no" << std::endl; #endif return buf.str(); } } // namespace bpftrace bpftrace-0.24.1/src/build_info.h000066400000000000000000000002141506776124200164450ustar00rootroot00000000000000#pragma once #include namespace bpftrace { class BuildInfo { public: static std::string report(); }; } // namespace bpftrace bpftrace-0.24.1/src/child.cpp000066400000000000000000000163011506776124200157550ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "child.h" #include "log.h" #include "util/paths.h" #include "util/strings.h" 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 { 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) { auto* 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 = util::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(); 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 = util::abs_path(path); if (!absolute.has_value()) continue; uniq_abs_path.insert(*absolute); } if (uniq_abs_path.size() == 1) { cmd[0] = paths.front(); 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 = util::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); const 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.24.1/src/child.h000066400000000000000000000054671506776124200154350ustar00rootroot00000000000000#pragma once #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() = 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() override; // 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() 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.24.1/src/config.cpp000066400000000000000000000264471506776124200161530ustar00rootroot00000000000000#include #include #include #include #include #include "config.h" #include "log.h" #include "types.h" #include "util/int_parser.h" #include "util/strings.h" namespace bpftrace { char ParseError::ID; char RenameError::ID; void ParseError::log(llvm::raw_ostream &OS) const { OS << key_ << ": " << detail_; } void RenameError::log(llvm::raw_ostream &OS) const { OS << "key has been renamed to '" << name_ << "'"; } // /proc/sys/kernel/randomize_va_space >= 1 static bool 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; } Config::Config(bool has_cmd) : user_symbol_cache_type((has_cmd || !is_aslr_enabled()) ? UserSymbolCacheType::per_program : UserSymbolCacheType::per_pid) {}; template <> struct ConfigParser { Result parse(const std::string &key, uint64_t *target, const std::string &s) { // If this can be parsed as a literal integer, then we take that. auto val = util::to_uint(s); if (!val) { return make_error(key, "expecting a number, got " + s); } *target = *val; return OK(); } Result parse([[maybe_unused]] const std::string &key, uint64_t *target, uint64_t v) { *target = v; return OK(); } }; template <> struct ConfigParser { Result parse(const std::string &key, bool *target, const std::string &original) { if (util::is_str_bool_truthy(original)) { *target = true; return OK(); } else if (util::is_str_bool_falsy(original)) { *target = false; return OK(); } else { return make_error( key, "Invalid bool value: valid values are true, false, 1 or 0."); } } Result parse([[maybe_unused]] const std::string &key, bool *target, uint64_t v) { if (v != 0) { *target = true; return OK(); } else { *target = false; return OK(); } } }; template <> struct ConfigParser { Result parse([[maybe_unused]] const std::string &key, std::string *target, const std::string &s) { *target = s; return OK(); } Result parse([[maybe_unused]] const std::string &key, std::string *target, uint64_t v) { std::stringstream ss; ss << v; *target = ss.str(); return OK(); } }; template <> struct ConfigParser { Result parse(const std::string &key, UserSymbolCacheType *target, const std::string &original) { std::string s = util::to_lower(original); if (s == "1") { return OK(); // Leave as the default. } else if (s == "per_pid") { *target = UserSymbolCacheType::per_pid; return OK(); } else if (s == "per_program") { *target = UserSymbolCacheType::per_program; return OK(); } else if (s == "none" || s == "0") { *target = UserSymbolCacheType::none; return OK(); } else { return make_error( key, "Invalid value for cache_user_symbols: valid values are PER_PID, " "PER_PROGRAM, and NONE."); } } Result parse([[maybe_unused]] const std::string &key, UserSymbolCacheType *target, uint64_t v) { if (v == 0) { *target = UserSymbolCacheType::none; return OK(); } else { // Leave as the default. return OK(); } } }; template <> struct ConfigParser { Result parse(const std::string &key, ConfigMissingProbes *target, const std::string &original) { std::string s = util::to_lower(original); if (s == "ignore") { *target = ConfigMissingProbes::ignore; return OK(); } else if (s == "warn") { *target = ConfigMissingProbes::warn; return OK(); } else if (s == "error") { *target = ConfigMissingProbes::error; return OK(); } else { return make_error(key, "Invalid value for missing_probes: valid " "values are ignore, warn, and error."); } } Result parse(const std::string &key, [[maybe_unused]] ConfigMissingProbes *target, [[maybe_unused]] uint64_t v) { return make_error(key, "Invalid value for missing_probes: valid " "values are ignore, warn, and error."); } }; template <> struct ConfigParser { Result parse(const std::string &key, ConfigUnstable *target, const std::string &original) { std::string s = util::to_lower(original); if (s == "enable" || util::is_str_bool_truthy(original)) { *target = ConfigUnstable::enable; return OK(); } else if (s == "warn") { *target = ConfigUnstable::warn; return OK(); } else if (s == "error" || util::is_str_bool_falsy(original)) { *target = ConfigUnstable::error; return OK(); } else { return make_error(key, "Invalid value for unstable config: valid " "values are enable, warn, and error."); } } Result parse([[maybe_unused]] const std::string &key, [[maybe_unused]] ConfigUnstable *target, [[maybe_unused]] uint64_t v) { if (v != 0) { *target = ConfigUnstable::enable; return OK(); } else { *target = ConfigUnstable::error; return OK(); } } }; struct AnyParser { using KeyType = const std::string &; std::function(KeyType, Config *, uint64_t)> integer; std::function(KeyType, Config *, const std::string &)> string; }; template AnyParser parser(T fn) { // The passed function should return a pointer to the field. We extract // the type of the returned field, and match against the parser. using R = std::remove_pointer_t(nullptr)))>; return AnyParser{ .integer = [fn](const std::string &k, Config *c, uint64_t value) { ConfigParser parser; return parser.parse(k, fn(c), value); }, .string = [fn](const std::string &k, Config *c, const std::string &s) { ConfigParser parser; return parser.parse(k, fn(c), s); }, }; } // This map construsts all the different parsers. #define CONFIG_FIELD_PARSER(x) parser([](Config *config) { return &config->x; }) const std::map CONFIG_KEY_MAP = { { "cache_user_symbols", CONFIG_FIELD_PARSER(user_symbol_cache_type) }, { "cpp_demangle", CONFIG_FIELD_PARSER(cpp_demangle) }, { "lazy_symbolication", CONFIG_FIELD_PARSER(lazy_symbolication) }, { "license", CONFIG_FIELD_PARSER(license) }, { "log_size", CONFIG_FIELD_PARSER(log_size) }, { "max_bpf_progs", CONFIG_FIELD_PARSER(max_bpf_progs) }, { "max_cat_bytes", CONFIG_FIELD_PARSER(max_cat_bytes) }, { "max_map_keys", CONFIG_FIELD_PARSER(max_map_keys) }, { "max_probes", CONFIG_FIELD_PARSER(max_probes) }, { "max_strlen", CONFIG_FIELD_PARSER(max_strlen) }, { "on_stack_limit", CONFIG_FIELD_PARSER(on_stack_limit) }, { "perf_rb_pages", CONFIG_FIELD_PARSER(perf_rb_pages) }, { "stack_mode", CONFIG_FIELD_PARSER(stack_mode) }, { "str_trunc_trailer", CONFIG_FIELD_PARSER(str_trunc_trailer) }, { "missing_probes", CONFIG_FIELD_PARSER(missing_probes) }, { "print_maps_on_exit", CONFIG_FIELD_PARSER(print_maps_on_exit) }, { "use_blazesym", CONFIG_FIELD_PARSER(use_blazesym) }, { "show_debug_info", CONFIG_FIELD_PARSER(show_debug_info) }, { UNSTABLE_IMPORT, CONFIG_FIELD_PARSER(unstable_import) }, { UNSTABLE_MACRO, CONFIG_FIELD_PARSER(unstable_macro) }, { UNSTABLE_MAP_DECL, CONFIG_FIELD_PARSER(unstable_map_decl) }, { UNSTABLE_TSERIES, CONFIG_FIELD_PARSER(unstable_tseries) }, { UNSTABLE_ADDR, CONFIG_FIELD_PARSER(unstable_addr) }, }; // These symbols are deprecated, and have been remapped elsewhere. const std::map DEPRECATED = { { "strlen", "max_strlen" }, { "no_cpp_demangle", "cpp_demangle" }, { "cat_bytes_max", "max_cat_bytes" }, { "map_keys_max", "max_map_keys" }, }; // These are configuration names that are consumed elsewhere. We use this only // to check if we should produce a more helpful error for the user. const std::unordered_set ENV_ONLY = { "btf", "debug_output", "kernel_build", "kernel_source", "max_ast_nodes", "verify_llvm_ir", "vmlinux", }; // This is applied for all environment variables, and will also be accepted // as part of the general configuration key (in lower case only). static const std::string ENV_PREFIX = "bpftrace_"; static std::string restore(const std::string &original_key) { std::string key = util::to_lower(original_key); if (key.starts_with(ENV_PREFIX)) { key = key.substr(ENV_PREFIX.length()); } return key; } static Result lookup(const std::string &original_key) { auto key = restore(original_key); auto it = CONFIG_KEY_MAP.find(key); if (it == CONFIG_KEY_MAP.end()) { auto dep = DEPRECATED.find(key); if (dep != DEPRECATED.end()) { return make_error(dep->second); } auto env = ENV_ONLY.find(key); if (env != ENV_ONLY.end()) { return make_error( original_key, "can only be set as an environment variable"); } return make_error(original_key, "not a known configuration option"); } return it->second; } Result Config::set(const std::string &original_key, const std::string &val) { auto parser = lookup(original_key); if (!parser) { return parser.takeError(); } return parser->string(original_key, this, val); } Result Config::set(const std::string &original_key, uint64_t val) { auto parser = lookup(original_key); if (!parser) { return parser.takeError(); } return parser->integer(original_key, this, val); } bool Config::is_unstable(const std::string &original_key) { const static std::string UNSTABLE_PREFIX = "unstable_"; auto key = restore(original_key); return key.starts_with(UNSTABLE_PREFIX); } Result Config::load_environment() { // Scan all known keys by their environment variable name, and if it is // present then set from the environment value. for (const auto &[key, _] : CONFIG_KEY_MAP) { std::string env = ENV_PREFIX + key; std::ranges::transform(env, env.begin(), [](unsigned char c) { return std::toupper(c); }); const auto *cenv = getenv(env.c_str()); if (cenv) { auto ok = set(key, std::string(cenv)); if (!ok) { return ok.takeError(); } } } return OK(); } } // namespace bpftrace bpftrace-0.24.1/src/config.h000066400000000000000000000053541506776124200156120ustar00rootroot00000000000000#pragma once #include #include #include "types.h" #include "util/result.h" namespace bpftrace { enum class ConfigMissingProbes { ignore, warn, error, }; enum class ConfigUnstable { enable, warn, error, }; static const auto UNSTABLE_IMPORT = "unstable_import"; static const auto UNSTABLE_MACRO = "unstable_macro"; static const auto UNSTABLE_MAP_DECL = "unstable_map_decl"; static const auto UNSTABLE_TSERIES = "unstable_tseries"; static const auto UNSTABLE_ADDR = "unstable_addr"; class Config { public: Config(bool has_cmd = false); // Fields can be accessed directly via the callers, although care should be // taken not to mutate these fields unless the caller truly intends to change // them. The `set` method is provided for external users to set the values. Result set(const std::string &key, uint64_t val); Result set(const std::string &key, const std::string &val); // Helpers for analysis of variables. bool is_unstable(const std::string &key); Result load_environment(); // All configuration options. bool cpp_demangle = true; bool lazy_symbolication = true; bool print_maps_on_exit = true; ConfigUnstable unstable_macro = ConfigUnstable::warn; ConfigUnstable unstable_map_decl = ConfigUnstable::warn; ConfigUnstable unstable_import = ConfigUnstable::warn; ConfigUnstable unstable_tseries = ConfigUnstable::warn; ConfigUnstable unstable_addr = ConfigUnstable::warn; #ifdef HAVE_BLAZESYM bool use_blazesym = true; bool show_debug_info = true; #else bool use_blazesym = false; bool show_debug_info = false; #endif uint64_t log_size = 1000000; uint64_t max_bpf_progs = 1024; uint64_t max_cat_bytes = 10240; uint64_t max_map_keys = 4096; uint64_t max_probes = 1024; uint64_t max_strlen = 1024; uint64_t on_stack_limit = 32; uint64_t perf_rb_pages = 64; std::string license = "GPL"; std::string str_trunc_trailer = ".."; ConfigMissingProbes missing_probes = ConfigMissingProbes::error; StackMode stack_mode = StackMode::bpftrace; // Initialized in the constructor. UserSymbolCacheType user_symbol_cache_type; }; // Specific key has been renamed, must be handled by caller. This may be // returned by the `Config::set` method if a different key must be used to set // the value. This is explicitly propagated in order to ensure that the caller // can appropriately propagate this information to the user. class RenameError : public ErrorInfo { public: static char ID; RenameError(std::string &&name) : name_(std::move(name)) {}; void log(llvm::raw_ostream &OS) const override; // Returns the new key which must be used. const std::string &new_name() const { return name_; } private: std::string name_; }; } // namespace bpftrace bpftrace-0.24.1/src/config_parser.h000066400000000000000000000014351506776124200171620ustar00rootroot00000000000000#pragma once #include #include "util/result.h" namespace bpftrace { // If you want to be able to parse custom configuration, then simply // provide a specialization of the `ConfigParser` class that specifies // the implementation of the operators to parse any string and integers. template struct ConfigParser; // Generic parse error for a specific key. This should be returned by // the `ConfigParser` implementations if the value cannot be parsed. class ParseError : public ErrorInfo { public: static char ID; ParseError(std::string key, std::string &&detail) : key_(std::move(key)), detail_(std::move(detail)) {}; void log(llvm::raw_ostream &OS) const override; private: std::string key_; std::string detail_; }; } // namespace bpftrace bpftrace-0.24.1/src/cxxdemangler/000077500000000000000000000000001506776124200166465ustar00rootroot00000000000000bpftrace-0.24.1/src/cxxdemangler/CMakeLists.txt000066400000000000000000000001741506776124200214100ustar00rootroot00000000000000add_library(cxxdemangler_stdlib STATIC cxxdemangler_stdlib.cpp) add_library(cxxdemangler_llvm STATIC cxxdemangler_llvm.cpp) bpftrace-0.24.1/src/cxxdemangler/cxxdemangler.h000066400000000000000000000002511506776124200214760ustar00rootroot00000000000000#pragma once namespace bpftrace { // Demangle a mangled C++ symbol name // // Note: callee `free()`ed char* cxxdemangle(const char* mangled); } // namespace bpftrace bpftrace-0.24.1/src/cxxdemangler/cxxdemangler_llvm.cpp000066400000000000000000000005261506776124200230700ustar00rootroot00000000000000#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.24.1/src/cxxdemangler/cxxdemangler_stdlib.cpp000066400000000000000000000003141506776124200233720ustar00rootroot00000000000000#include "cxxdemangler.h" #include namespace bpftrace { char* cxxdemangle(const char* mangled) { return abi::__cxa_demangle(mangled, nullptr, nullptr, nullptr); } } // namespace bpftrace bpftrace-0.24.1/src/debugfs/000077500000000000000000000000001506776124200156045ustar00rootroot00000000000000bpftrace-0.24.1/src/debugfs/CMakeLists.txt000066400000000000000000000000561506776124200203450ustar00rootroot00000000000000add_library(debugfs STATIC debugfs.cpp ) bpftrace-0.24.1/src/debugfs/debugfs.cpp000066400000000000000000000004071506776124200177300ustar00rootroot00000000000000#include #include "debugfs.h" 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.24.1/src/debugfs/debugfs.h000066400000000000000000000003621506776124200173750ustar00rootroot00000000000000#pragma once #include namespace bpftrace::debugfs { std::string path(); std::string path(const std::string &file); inline std::string kprobes_blacklist() { return path("kprobes/blacklist"); } } // namespace bpftrace::debugfs bpftrace-0.24.1/src/disasm.cpp000066400000000000000000000007561506776124200161610ustar00rootroot00000000000000#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.24.1/src/disasm.h000066400000000000000000000007701506776124200156220ustar00rootroot00000000000000#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.24.1/src/driver.cpp000066400000000000000000000045561506776124200161760ustar00rootroot00000000000000#include "driver.h" extern void set_source_string(const std::string *s); extern int yylex_init(yyscan_t *scanner); extern int yylex_destroy(yyscan_t yyscanner); namespace bpftrace { void Driver::parse(Parser::symbol_type first_token) { // Reset state on every pass. loc.initialize(); struct_type.clear(); buffer.clear(); // Push the start token, which indicates that exact context that we should // now be parsing. token.emplace(first_token); yyscan_t scanner; yylex_init(&scanner); Parser parser(*this, scanner); if (debug) { parser.set_debug_level(1); } set_source_string(&ctx.source_->contents); parser.parse(); yylex_destroy(scanner); } ast::Program *Driver::parse_program() { parse(Parser::make_START_PROGRAM(loc)); if (std::holds_alternative(result)) { return std::get(result); } return nullptr; } std::optional Driver::parse_expr() { parse(Parser::make_START_EXPR(loc)); if (std::holds_alternative(result)) { return std::get(result); } return std::nullopt; } void Driver::error(const location &l, const std::string &m) { // This path is normally not allowed, however we don't yet have nodes // constructed. Therefore, we add diagnostics directly via the private field. ctx.state_->diagnostics_->addError(ctx.wrap(l)) << m; } ast::Pass CreateParsePass(uint32_t max_ast_nodes, bool debug) { return ast::Pass::create("parse", [=](ast::ASTContext &ast) { Driver driver(ast, debug); ast.root = driver.parse_program(); // 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. if (ast.diagnostics().ok()) { assert(ast.root != nullptr); auto node_count = ast.node_count(); if (max_ast_nodes > 0 && node_count > max_ast_nodes) { ast.root->addError() << "node count (" << node_count << ") exceeds the limit (" << max_ast_nodes << ")"; } } }); } } // namespace bpftrace bpftrace-0.24.1/src/driver.h000066400000000000000000000023531506776124200156340ustar00rootroot00000000000000#pragma once #include #include "ast/ast.h" #include "ast/context.h" #include "ast/pass_manager.h" #include "parser.tab.hh" using yyscan_t = void *; #define YY_DECL \ bpftrace::Parser::symbol_type yylex(bpftrace::Driver &driver, \ yyscan_t yyscanner) namespace bpftrace { class Driver { public: explicit Driver(ast::ASTContext &ctx, bool debug = false) : ctx(ctx), debug(debug) {}; ast::Program *parse_program(); std::optional parse_expr(); void error(const location &l, const std::string &m); // These are accessible to the parser and lexer, but are not mutable. ast::ASTContext &ctx; const bool debug; // These are mutable state that can be modified by the lexer. location loc; std::string struct_type; std::string buffer; // This is the token injected into the lexer. std::optional token; // The final result is available here. std::variant result; private: void parse(Parser::symbol_type first_token); }; ast::Pass CreateParsePass(uint32_t max_ast_nodes = 0, bool debug = false); } // namespace bpftrace bpftrace-0.24.1/src/dwarf_parser.cpp000066400000000000000000000304621506776124200173550ustar00rootroot00000000000000#include "dwarf_parser.h" #ifdef HAVE_LIBDW #include "bpftrace.h" #include "log.h" #include #include namespace bpftrace { struct FuncInfo { std::string name; Dwarf_Die die; }; Dwarf::Dwarf(BPFtrace *bpftrace, const std::string &file_path) : bpftrace_(bpftrace), file_path_(file_path) { callbacks.find_debuginfo = dwfl_standard_find_debuginfo; callbacks.section_address = dwfl_offline_section_address; callbacks.debuginfo_path = nullptr; dwfl = dwfl_begin(&callbacks); dwfl_report_offline(dwfl, file_path.c_str(), file_path.c_str(), -1); dwfl_report_end(dwfl, nullptr, nullptr); } std::unique_ptr Dwarf::GetFromBinary(BPFtrace *bpftrace, const std::string &file_path) { std::unique_ptr dwarf(new Dwarf(bpftrace, file_path)); Dwarf_Addr bias; if (dwfl_nextcu(dwarf->dwfl, nullptr, &bias) == nullptr) return nullptr; return dwarf; } Dwarf::~Dwarf() { dwfl_end(dwfl); } static int get_func_die_cb(Dwarf_Die *func_die, void *arg) { auto *func_info = static_cast(arg); if (dwarf_hasattr(func_die, DW_AT_name) && dwarf_diename(func_die) == func_info->name) { func_info->die = *func_die; return DWARF_CB_ABORT; } return DWARF_CB_OK; } std::optional Dwarf::get_func_die(const std::string &function) const { struct FuncInfo func_info = { .name = function, .die = {} }; Dwarf_Die *cudie = nullptr; Dwarf_Addr cubias; while ((cudie = dwfl_nextcu(dwfl, cudie, &cubias)) != nullptr) { if (dwarf_getfuncs(cudie, get_func_die_cb, &func_info, 0) > 0) return func_info.die; } return std::nullopt; } static Dwarf_Die type_of(Dwarf_Die &die) { Dwarf_Attribute attr; Dwarf_Die type_die; dwarf_formref_die(dwarf_attr_integrate(&die, DW_AT_type, &attr), &type_die); return type_die; } std::vector Dwarf::function_param_dies( const std::string &function) const { auto func_die = get_func_die(function); if (!func_die) return {}; return get_all_children_with_tag(&func_die.value(), DW_TAG_formal_parameter); } std::string Dwarf::get_type_name(Dwarf_Die &type_die) const { auto tag = dwarf_tag(&type_die); switch (tag) { case DW_TAG_base_type: case DW_TAG_typedef: return dwarf_diename(&type_die); case DW_TAG_pointer_type: { if (dwarf_hasattr(&type_die, DW_AT_type)) { Dwarf_Die inner_type = type_of(type_die); return get_type_name(inner_type) + "*"; } return "void*"; } case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_enumeration_type: { std::string prefix; if (tag == DW_TAG_structure_type) prefix = "struct "; else if (tag == DW_TAG_union_type) prefix = "union "; else prefix = "enum "; if (dwarf_hasattr(&type_die, DW_AT_name)) return prefix + dwarf_diename(&type_die); else return prefix + ""; } case DW_TAG_const_type: { Dwarf_Die inner_type = type_of(type_die); if (dwarf_tag(&inner_type) == DW_TAG_pointer_type) return get_type_name(inner_type) + " const"; else return "const " + get_type_name(inner_type); } default: return ""; } } Dwarf_Word Dwarf::get_type_encoding(Dwarf_Die &type_die) const { Dwarf_Attribute encoding_attr; Dwarf_Word encoding; dwarf_formudata( dwarf_attr_integrate(&type_die, DW_AT_encoding, &encoding_attr), &encoding); return encoding; } SizedType Dwarf::get_stype(Dwarf_Die &type_die, bool resolve_structs) const { Dwarf_Die type; dwarf_peel_type(&type_die, &type); auto tag = dwarf_tag(&type); auto bit_size = dwarf_hasattr(&type, DW_AT_bit_size) ? dwarf_bitsize(&type) : dwarf_bytesize(&type) * 8; switch (tag) { case DW_TAG_base_type: { Dwarf_Word encoding = get_type_encoding(type); switch (encoding) { case DW_ATE_boolean: case DW_ATE_unsigned: case DW_ATE_unsigned_char: return CreateUInt(bit_size); case DW_ATE_signed: case DW_ATE_signed_char: return CreateInt(bit_size); default: return CreateNone(); } } case DW_TAG_enumeration_type: return CreateUInt(bit_size); case DW_TAG_pointer_type: { if (dwarf_hasattr(&type, DW_AT_type)) { Dwarf_Die inner_type = type_of(type); return CreatePointer(get_stype(inner_type, false)); } // void * return CreatePointer(CreateNone()); } case DW_TAG_structure_type: case DW_TAG_union_type: { std::string name = dwarf_diename(&type_die); name = (tag == DW_TAG_structure_type ? "struct " : "union ") + name; auto result = CreateRecord( name, bpftrace_->structs.LookupOrAdd(name, bit_size / 8)); if (resolve_structs) resolve_fields(result); return result; } case DW_TAG_array_type: { Dwarf_Die inner_type_die = type_of(type_die); Dwarf_Word inner_enc = get_type_encoding(inner_type_die); SizedType inner_type = get_stype(inner_type_die); SizedType result; for (auto &d : get_all_children_with_tag(&type_die, DW_TAG_subrange_type)) { ssize_t size = get_array_size(d); if (dwarf_tag(&inner_type_die) == DW_TAG_base_type && (inner_enc == DW_ATE_signed_char || inner_enc == DW_ATE_unsigned_char)) // See btf.cpp; we need to signal well-formedness. result = CreateString(size + 1); else result = CreateArray(size, inner_type); inner_type = result; } return result; } default: return CreateNone(); } } SizedType Dwarf::get_stype(const std::string &type_name) const { std::string name = type_name; if (name.starts_with("struct ")) name = name.substr(strlen("struct ")); auto type_die = find_type(name); if (!type_die) return CreateNone(); return get_stype(type_die.value()); } std::optional Dwarf::resolve_bitfield(Dwarf_Die &field_die) const { ssize_t bitfield_width = get_bitfield_size(field_die); if (bitfield_width == 0) return std::nullopt; ssize_t bit_offset = get_field_bit_offset(field_die); if (dwarf_hasattr(&field_die, DW_AT_data_bit_offset)) { // DWARF >= 4 // DW_AT_data_bit_offset is the offset in bits from the beginning of the // containing entity to the beginning of field. In this case, the byte // offset of the field is determined by (bit_offset / 8) so the bit offset // within the byte is given by (bit_offset % 8). return Bitfield(bit_offset % 8, bitfield_width); } else { // DWARF < 4 (some implementations of DWARF 4 use this, too) // Bit offset is given by DW_AT_bit_offset which is the offset in bits of // the high order bit of the container type to the high order bit of the // storage unit actually containing the bitfield. This representation was // designed for big-endian systems, so we must use the same approach to // determine the actual bit offset: // (size of the container field - DW_AT_bit_offset - bitfield size) auto field_size = dwarf_bytesize(&field_die) * 8; return Bitfield(field_size - bit_offset - bitfield_width, bitfield_width); } } void Dwarf::resolve_fields(const SizedType &type) const { if (!type.IsRecordTy()) return; auto str = bpftrace_->structs.Lookup(type.GetName()).lock(); if (str->HasFields()) return; std::string type_name = type.GetName(); if (type_name.starts_with("struct ")) type_name = type_name.substr(strlen("struct ")); auto type_die = find_type(type_name); if (!type_die) return; for (auto &field_die : get_all_children_with_tag(&type_die.value(), DW_TAG_member)) { Dwarf_Die field_type = type_of(field_die); str->AddField(dwarf_diename(&field_die), get_stype(field_type), get_field_byte_offset(field_die), resolve_bitfield(field_die), false); } } std::vector Dwarf::get_function_params( const std::string &function) const { std::vector result; for (auto ¶m_die : function_param_dies(function)) { Dwarf_Die type_die = type_of(param_die); const std::string type_name = get_type_name(type_die); if (dwarf_hasattr(¶m_die, DW_AT_name)) result.push_back(type_name + " " + dwarf_diename(¶m_die)); else result.push_back(type_name); } return result; } std::shared_ptr Dwarf::resolve_args(const std::string &function) { auto result = std::make_shared(0, false); int i = 0; for (auto ¶m_die : function_param_dies(function)) { Dwarf_Die type_die = type_of(param_die); SizedType arg_type = get_stype(type_die); arg_type.is_funcarg = true; arg_type.funcarg_idx = i++; const std::string name = dwarf_hasattr(¶m_die, DW_AT_name) ? dwarf_diename(¶m_die) : ""; result->AddField(name, arg_type, result->size, std::nullopt, false); result->size += arg_type.GetSize(); } return result; } std::optional Dwarf::find_type(const std::string &name) const { Dwarf_Die *cudie = nullptr; Dwarf_Addr cubias; while ((cudie = dwfl_nextcu(dwfl, cudie, &cubias)) != nullptr) { if (auto type_die = get_child_with_tagname(cudie, DW_TAG_structure_type, name)) return type_die; } return std::nullopt; } std::optional Dwarf::get_child_with_tagname(Dwarf_Die *die, int tag, const std::string &name) { Dwarf_Die child_die; Dwarf_Die *child_iter = &child_die; if (dwarf_child(die, &child_die) != 0) return std::nullopt; do { if (dwarf_tag(&child_die) == tag && dwarf_hasattr(&child_die, DW_AT_name) && dwarf_diename(&child_die) == name) return child_die; } while (dwarf_siblingof(child_iter, &child_die) == 0); return std::nullopt; } std::vector Dwarf::get_all_children_with_tag(Dwarf_Die *die, int tag) { Dwarf_Die child_die; Dwarf_Die *child_iter = &child_die; if (dwarf_child(die, &child_die) != 0) return {}; std::vector children; do { if (dwarf_tag(&child_die) == tag) children.push_back(child_die); } while (dwarf_siblingof(child_iter, &child_die) == 0); return children; } ssize_t Dwarf::get_array_size(Dwarf_Die &subrange_die) { Dwarf_Attribute size_attr; Dwarf_Word size; if (dwarf_hasattr(&subrange_die, DW_AT_upper_bound)) { dwarf_formudata( dwarf_attr_integrate(&subrange_die, DW_AT_upper_bound, &size_attr), &size); return static_cast(size) + 1; } if (dwarf_hasattr(&subrange_die, DW_AT_count)) { dwarf_formudata( dwarf_attr_integrate(&subrange_die, DW_AT_count, &size_attr), &size); return static_cast(size); } return 0; } ssize_t Dwarf::get_field_byte_offset(Dwarf_Die &field_die) { if (dwarf_hasattr(&field_die, DW_AT_data_member_location)) { Dwarf_Attribute attr; Dwarf_Word value; if (dwarf_formudata( dwarf_attr_integrate(&field_die, DW_AT_data_member_location, &attr), &value) >= 0) return static_cast(value); } return get_field_bit_offset(field_die) / 8; } ssize_t Dwarf::get_field_bit_offset(Dwarf_Die &field_die) { Dwarf_Attribute attr; Dwarf_Word value; if (dwarf_hasattr(&field_die, DW_AT_data_bit_offset)) { // DWARF >= 4 if (dwarf_formudata( dwarf_attr_integrate(&field_die, DW_AT_data_bit_offset, &attr), &value) >= 0) return static_cast(value); } if (dwarf_hasattr(&field_die, DW_AT_bit_offset)) { // DWARF < 4 if (dwarf_formudata( dwarf_attr_integrate(&field_die, DW_AT_bit_offset, &attr), &value) >= 0) return static_cast(value); } return 0; } ssize_t Dwarf::get_bitfield_size(Dwarf_Die &field_die) { Dwarf_Attribute attr; Dwarf_Word value; if (dwarf_hasattr(&field_die, DW_AT_bit_size)) { if (dwarf_formudata(dwarf_attr_integrate(&field_die, DW_AT_bit_size, &attr), &value) >= 0) return static_cast(value); } return 0; } } // namespace bpftrace #endif // HAVE_LIBDW bpftrace-0.24.1/src/dwarf_parser.h000066400000000000000000000055541506776124200170260ustar00rootroot00000000000000#pragma once #include "struct.h" #include "types.h" #include #include #include #ifdef HAVE_LIBDW #include #include #include namespace bpftrace { class BPFtrace; class Dwarf { public: virtual ~Dwarf(); static std::unique_ptr GetFromBinary(BPFtrace *bpftrace, const std::string &file_path); std::vector get_function_params( const std::string &function) const; std::shared_ptr resolve_args(const std::string &function); SizedType get_stype(const std::string &type_name) const; void resolve_fields(const SizedType &type) const; private: Dwarf(BPFtrace *bpftrace, const std::string &file_path); std::vector function_param_dies(const std::string &function) const; std::optional get_func_die(const std::string &function) const; std::string get_type_name(Dwarf_Die &type_die) const; Dwarf_Word get_type_encoding(Dwarf_Die &type_die) const; std::optional find_type(const std::string &name) const; static ssize_t get_array_size(Dwarf_Die &subrange_die); static ssize_t get_field_byte_offset(Dwarf_Die &field_die); static ssize_t get_field_bit_offset(Dwarf_Die &field_die); static ssize_t get_bitfield_size(Dwarf_Die &field_die); std::optional resolve_bitfield(Dwarf_Die &field_die) const; SizedType get_stype(Dwarf_Die &type_die, bool resolve_structs = true) const; static std::optional get_child_with_tagname( Dwarf_Die *die, int tag, const std::string &name); static std::vector get_all_children_with_tag(Dwarf_Die *die, int tag); Dwfl *dwfl = nullptr; Dwfl_Callbacks callbacks; BPFtrace *bpftrace_; std::string file_path_; }; } // namespace bpftrace #else // HAVE_LIBDW #include "log.h" namespace bpftrace { class BPFtrace; class Dwarf { public: static std::unique_ptr GetFromBinary(BPFtrace *bpftrace __attribute__((unused)), const std::string &file_path_ __attribute__((unused))) { return nullptr; } std::vector get_function_params(const std::string &function __attribute__((unused))) const { return {}; } std::shared_ptr resolve_args(const std::string &function __attribute__((unused))) { return nullptr; } SizedType get_stype(const std::string &type_name __attribute__((unused))) const { return CreateNone(); } void resolve_fields(const SizedType &type __attribute__((unused))) const { } }; } // namespace bpftrace #endif // HAVE_LIBDW bpftrace-0.24.1/src/format_string.cpp000066400000000000000000000256071506776124200175610ustar00rootroot00000000000000#include #include #include "format_string.h" #include "output/output.h" #include "struct.h" #include "util/exceptions.h" #include "util/strings.h" namespace bpftrace { using namespace output; char FormatError::ID; void FormatError::log(llvm::raw_ostream& OS) const { OS << msg_; } // N.B. the `r`, `rh` and `rx` specifiers are non-standard and also have // modifiers that *follow* the primary specifier. This means that they need // special cases, and the ordering matters to ensure that we capture the // modifiers if they are present (so `r` is the final match in the list). const std::regex FormatSpec::regex( R"(%(-?)(\+?)( ?)(#?)(0?)(\*|\d+)?(?:\.(\*|\d+))?([hlLjzt]*)([diouxXeEfFgGaAcspn%]|rh|rx|r))"); FormatSpec::FormatSpec(const std::smatch& match) { left_align = !match[1].str().empty(); show_sign = !match[2].str().empty(); space_prefix = !match[3].str().empty(); alternate_form = !match[4].str().empty(); lead_zeros = !match[5].str().empty(); if (!match[6].str().empty() && match[6].str() != "*") { width = std::stoi(match[6].str()); } if (!match[7].str().empty() && match[7].str() != "*") { precision = std::stoi(match[7].str()); } length_modifier = match[8].str(); specifier = match[9].str(); } FormatString::FormatString() = default; FormatString::FormatString(const std::string& fmt) : fmt_(std::move(fmt)) { parse(); } FormatString::~FormatString() = default; void FormatString::parse() { fragments.clear(); specs.clear(); auto begin = std::sregex_iterator(fmt_.begin(), fmt_.end(), FormatSpec::regex); auto end = std::sregex_iterator(); size_t last_match_end = 0; std::stringstream last_fragment; for (auto it = begin; it != end; ++it) { last_fragment << fmt_.substr(last_match_end, it->position() - last_match_end); last_match_end = it->position() + it->length(); auto spec = FormatSpec(*it); if (spec.specifier == "%") { last_fragment << "%"; continue; } fragments.emplace_back(last_fragment.str()); last_fragment.str(""); // Reset the fragment. specs.emplace_back(std::move(spec)); } last_fragment << fmt_.substr(last_match_end); fragments.emplace_back(last_fragment.str()); } Result<> FormatString::check(const std::vector& args) const { std::stringstream err; if (args.size() < specs.size()) { err << "not enough arguments for format string (" << args.size() << " supplied, " << specs.size() << " expected)"; return make_error(err.str()); } if (args.size() > specs.size()) { err << "too many arguments for format string (" << args.size() << " supplied, " << specs.size() << " expected)"; return make_error(err.str()); } // Walk over the arguments and check the most basic type information. static const std::vector any_integer = { Type::integer, Type::boolean, Type::pointer }; static const std::map> required_type = { { "d", any_integer }, { "i", any_integer }, { "u", any_integer }, { "o", any_integer }, { "x", any_integer }, { "X", any_integer }, { "c", any_integer }, { "r", { Type::buffer } }, { "rx", { Type::buffer } }, { "rh", { Type::buffer } }, { "s", {} }, { "p", any_integer }, }; for (size_t i = 0; i < specs.size(); i++) { if (args[i].IsNoneTy()) { err << "unable to print none type for specifier: " << specs[i].specifier; return make_error(err.str()); } auto it = required_type.find(specs[i].specifier); if (it == required_type.end()) { err << "unsupported format specifier: " << specs[i].specifier; return make_error(err.str()); } if (it->second.empty()) { // Anything goes. continue; } bool found = false; for (const auto& allowed : it->second) { if (args[i].GetTy() == allowed) { found = true; break; } } if (!found) { err << "unsupported format specifier for type '" << typestr(args[i]) << "': " << specs[i].specifier; return make_error(err.str()); } } return OK(); } template Result<> as_number(std::stringstream& ss, const Primitive& p) { return std::visit( [&](const auto& v) -> Result<> { if constexpr (std::is_same_v, output::Primitive::Symbolic>) { return as_number(ss, output::Primitive(v.numeric)); } else if constexpr (std::is_same_v, int64_t> || std::is_same_v, uint64_t> || std::is_same_v, double> || std::is_same_v, bool>) { if constexpr (std::is_same_v) { ss << reinterpret_cast(static_cast(v)); } else { ss << static_cast(static_cast(v)); } return OK(); } else { std::stringstream msg; msg << "invalid integer conversion: " << p; return make_error(msg.str()); } }, p.variant); } template Result<> as_signed_integer(std::stringstream& ss, const Primitive& p, const std::string& length_modifier) { if (length_modifier == "hh") { return as_number(ss, p); } else if (length_modifier == "h") { return as_number(ss, p); } else if (length_modifier == "l") { return as_number(ss, p); } else if (length_modifier == "ll") { return as_number(ss, p); } else if (length_modifier == "j") { return as_number(ss, p); } else if (length_modifier == "z") { return as_number(ss, p); } else if (length_modifier == "t") { return as_number(ss, p); } else { return as_number(ss, p); } } template Result<> as_unsigned_integer(std::stringstream& ss, const Primitive& p, const std::string& length_modifier) { if (length_modifier == "hh") { return as_number(ss, p); } else if (length_modifier == "h") { return as_number(ss, p); } else if (length_modifier == "l") { return as_number(ss, p); } else if (length_modifier == "ll") { return as_number(ss, p); } else if (length_modifier == "j") { return as_number(ss, p); } else if (length_modifier == "z") { return as_number(ss, p); } else if (length_modifier == "t") { return as_number(ss, p); } else { return as_number(ss, p); } } Result<> as_floating_point(std::stringstream& ss, const Primitive& p, const std::string& length_modifier) { if (length_modifier == "L") { return as_number(ss, p); } else { return as_number(ss, p); } } static Result<> as_string(std::stringstream& ss, const Primitive& p, [[maybe_unused]] const std::string& length_modifier) { ss << p; return OK(); } template static Result<> as_buffer(std::stringstream& ss, const Primitive& p, const std::string& length_modifier) { if (std::holds_alternative(p.variant)) { const auto& buf = std::get(p.variant); ss << util::hex_format_buffer( buf.data.data(), buf.data.size(), keep_ascii, escape_hex); return OK(); } else { return as_string(ss, p, length_modifier); } } Result FormatSpec::apply(const Primitive& p) const { std::stringstream ss; if (left_align) { ss << std::left; } else { ss << std::right; } if (width > 0) { ss << std::setw(width); } if (lead_zeros) { ss << std::setfill('0'); } if (precision >= 0 && (specifier == "f" || specifier == "F" || specifier == "e" || specifier == "E" || specifier == "g" || specifier == "G" || specifier == "a" || specifier == "A")) { ss << std::setprecision(precision); if (specifier == "f" || specifier == "F") { ss << std::fixed; } else if (specifier == "e" || specifier == "E") { ss << std::scientific; } } if (specifier == "o") { ss << std::oct; } else if (specifier == "x") { ss << std::hex << std::nouppercase; } else if (specifier == "X") { ss << std::hex << std::uppercase; } if (alternate_form) { ss << std::showbase; } if (show_sign) { ss << std::showpos; } using SpecifierHandler = Result<> (*)(std::stringstream&, const Primitive&, const std::string&); static const std::map specifier_dispatch = { { "d", as_signed_integer }, { "i", as_signed_integer }, { "u", as_unsigned_integer }, { "o", as_unsigned_integer }, { "x", as_unsigned_integer }, { "X", as_unsigned_integer }, { "f", as_floating_point }, { "F", as_floating_point }, { "e", as_floating_point }, { "E", as_floating_point }, { "g", as_floating_point }, { "G", as_floating_point }, { "a", as_floating_point }, { "A", as_floating_point }, { "c", as_signed_integer }, { "r", as_buffer }, { "rx", as_buffer }, { "rh", as_buffer }, { "s", as_string }, { "p", as_unsigned_integer }, }; auto* dispatcher = as_string; // Default. auto it = specifier_dispatch.find(specifier); if (it != specifier_dispatch.end()) { dispatcher = it->second; } auto ok = dispatcher(ss, p, length_modifier); if (!ok) { return ok.takeError(); } return ss.str(); } std::string FormatString::format(const std::vector& args) const { std::stringstream ss; for (size_t i = 0; i < args.size(); i++) { ss << fragments[i]; auto s = specs[i].apply(args[i]); if (s) { // Write the formatted string. ss << *s; } else { // Nothing has been written, so just embed the error into the string here. // This is what happens in `Go` when a value cannot be formatted properly. ss << "!{" << s.takeError() << "}"; } } ss << fragments.back(); return ss.str(); } } // namespace bpftrace bpftrace-0.24.1/src/format_string.h000066400000000000000000000044351506776124200172220ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "types.h" namespace bpftrace { class FormatError : public ErrorInfo { public: FormatError(std::string msg) : msg_(std::move(msg)) {}; static char ID; void log(llvm::raw_ostream &OS) const override; private: std::string msg_; }; namespace output { struct Primitive; } // namespace output // FormatSpec is a parsed format token. class FormatSpec { public: bool left_align = false; // - bool show_sign = false; // + bool space_prefix = false; // (space) bool alternate_form = false; // # bool lead_zeros = false; // 0 int width = 0; // field width int precision = -1; // precision after decimal point std::string length_modifier; // h, l, ll, etc. std::string specifier; // d, s, x, etc. private: static const std::regex regex; FormatSpec(const std::smatch &match); Result apply(const output::Primitive &p) const; friend class FormatString; }; class FormatString { public: FormatString(); FormatString(const std::string &fmt); ~FormatString(); // check can be used to check if the format is valid, given a set of arguments // passed in. This does a best effort analysis based on the types. Result<> check(const std::vector &args) const; // 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. std::string format(const std::vector &args) const; // returns the original format string. const std::string &str() const { return fmt_; } // These may be used by callers to do manual validation. The fragments must be // exactly one element larger than the specs, and the sequence that is // constructed is: (fragment, spec, fragment, ..., spec, fragment). std::vector fragments; std::vector specs; private: std::string fmt_; // parses the internal format string. void parse(); friend class cereal::access; template void serialize(Archive &ar) { ar(fmt_); if (fragments.empty() && specs.empty()) { parse(); } } }; } // namespace bpftrace bpftrace-0.24.1/src/functions.cpp000066400000000000000000000110741506776124200167040ustar00rootroot00000000000000#include "functions.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].emplace_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, const ast::Node &node) 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()) { node.addError() << "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; } auto &err = node.addError(); err << "Cannot call function '" << name << "' using argument types: " << arg_types_str(arg_types); err.addHint() << "Candidate function:\n " << candidate->name() << param_types_str(candidate->params()); return nullptr; } } // namespace bpftrace bpftrace-0.24.1/src/functions.h000066400000000000000000000063541506776124200163560ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "ast/ast.h" #include "types.h" #include "util/hash.h" namespace bpftrace { // A parameter for a BpfScript function class Param { public: Param(std::string name, SizedType type) : name_(std::move(name)), type_(std::move(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, SizedType return_type, const std::vector ¶ms) : name_(std::move(name)), return_type_(std::move(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, const ast::Node &node) 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 { std::size_t seed = 0; util::hash_combine(seed, fq_name.ns); util::hash_combine(seed, fq_name.name); return seed; } }; std::unordered_map>, HashFqName> funcs_by_fq_name_; std::vector> all_funcs_; }; } // namespace bpftrace bpftrace-0.24.1/src/globalvars.cpp000066400000000000000000000432231506776124200170310ustar00rootroot00000000000000#include #include #include #include #include "ast/passes/named_param.h" #include "bpftrace.h" #include "globalvars.h" #include "log.h" #include "required_resources.h" #include "types.h" #include "util/exceptions.h" #include "util/int_parser.h" #include "util/strings.h" namespace bpftrace::globalvars { char NamedParamError::ID; void NamedParamError::log(llvm::raw_ostream &OS) const { OS << "program command line option --" << param_ << " " << err_ << " (value: " << value_ << ")\n"; } char UnknownParamError::ID; void UnknownParamError::log(llvm::raw_ostream &OS) const { OS << err(); } static Type get_global_var_type(const GlobalVarValue &value) { if (std::holds_alternative(value)) { return Type::integer; } else if (std::holds_alternative(value)) { return Type::boolean; } else if (std::holds_alternative(value)) { return Type::string; } else { LOG(BUG) << "Unknown global variable type"; return Type::none; } } 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; } uint16_t 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 name = std::string( 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 it = vars_and_offsets.find(name); if (it != vars_and_offsets.end()) { it->second = member->offset; } } for (const auto &[global_var, offset] : vars_and_offsets) { if (offset < 0) { LOG(BUG) << "Global variable " << global_var << " has not been added to the BPF code " "(codegen_llvm)"; } } return vars_and_offsets; } void update_global_vars_rodata( struct bpf_map *global_vars_map, const std::unordered_map &added_global_vars, const std::map &vars_and_offsets, const GlobalVarMap &global_var_vals) { size_t v_size; char *global_vars_buf = reinterpret_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) { auto it = global_var_vals.find(global_var); if (it != global_var_vals.end()) { const auto &config = added_global_vars.at(global_var); if (config.type == Type::integer) { auto *var = reinterpret_cast(global_vars_buf + offset); *var = std::get(it->second); } else if (config.type == Type::boolean) { auto *var = reinterpret_cast(global_vars_buf + offset); *var = std::get(it->second) ? 1 : 0; } else if (config.type == Type::string) { auto *var = reinterpret_cast(global_vars_buf + offset); const auto &val = std::get(it->second); strncpy(var, val.c_str(), val.size() + 1); } else { LOG(BUG) << "Unsupported global variable type " << config.type; } } } } void update_global_vars_custom_rw_section( const std::string §ion_name, struct bpf_map *global_vars_map, const std::map &vars_and_offsets, const std::unordered_set &needed_global_vars, int max_cpu_id) { 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 = (max_cpu_id + 1) * actual_size; auto err = bpf_map__set_value_size(global_vars_map, desired_size); if (err != 0) { throw util::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 util::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 if (vars_and_offsets.at(global_var) != 0) { LOG(BUG) << "Read-write global variable " << global_var << " must be at offset 0 in section " << section_name; } } 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 GlobalVars::get_sized_type(const std::string &global_var_name, const RequiredResources &resources, const Config &bpftrace_config) const { const auto &config = get_config(global_var_name); if (global_var_name == FMT_STRINGS_BUFFER) { assert(resources.max_fmtstring_args_size > 0); return make_rw_type( 1, CreateArray(resources.max_fmtstring_args_size, CreateInt8())); } if (global_var_name == 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())); } if (global_var_name == GET_STR_BUFFER) { assert(resources.str_buffers > 0); const auto max_strlen = bpftrace_config.max_strlen; return make_rw_type(resources.str_buffers, CreateArray(max_strlen, CreateInt8())); } if (global_var_name == 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())); } if (global_var_name == 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())); } if (global_var_name == 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())); } if (global_var_name == 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())); } if (global_var_name == EVENT_LOSS_COUNTER) { return make_rw_type(1, CreateUInt64()); } if (config.type == Type::integer) { return CreateInt64(); } if (config.type == Type::boolean) { return CreateInt8(); } if (config.type == Type::string) { const auto max_strlen = bpftrace_config.max_strlen; return make_rw_type(1, CreateArray(max_strlen, CreateInt8())); } LOG(BUG) << "Unknown global variable " << global_var_name; return CreateInt64(); } void GlobalVars::check_index(const std::string &global_var_name, const RequiredResources &resources, size_t index) const { if (global_var_name == TUPLE_BUFFER) { assert(index < resources.tuple_buffers); } else if (global_var_name == GET_STR_BUFFER) { assert(index < resources.str_buffers); } else if (global_var_name == READ_MAP_VALUE_BUFFER) { assert(index < resources.read_map_value_buffers); } else if (global_var_name == VARIABLE_BUFFER) { assert(index < resources.variable_buffers); } else if (global_var_name == MAP_KEY_BUFFER) { assert(index < resources.map_key_buffers); } } std::unordered_set get_section_names() { std::unordered_set ret; for (const auto &[_, config] : GLOBAL_VAR_CONFIGS) { ret.insert(config.section); } return ret; } void GlobalVars::add_known(const std::string_view &name) { if (!GLOBAL_VAR_CONFIGS.contains(name)) { LOG(BUG) << "Unknown global variable: " << name; } auto str_name = std::string(name); if (added_global_vars_.contains(str_name)) { return; } added_global_vars_[std::move(str_name)] = GLOBAL_VAR_CONFIGS.at(name); } void GlobalVars::add_named_param(const std::string &name, const GlobalVarValue &default_val) { if (added_global_vars_.contains(name)) { return; } added_global_vars_[name] = GlobalVarConfig({ .section = std::string(RO_SECTION_NAME), .type = get_global_var_type(default_val), }); named_param_defaults_[name] = default_val; } const GlobalVarConfig &GlobalVars::get_config(const std::string &name) const { auto it = added_global_vars_.find(name); if (it == added_global_vars_.end()) { LOG(BUG) << "Unknown global variable: " << name; } return it->second; } void GlobalVars::verify_maps_found( const std::unordered_map §ion_name_to_global_vars_map) { for (const auto &[name, config] : added_global_vars_) { if (!section_name_to_global_vars_map.contains(config.section)) { LOG(BUG) << "No map found in " << config.section << " which is needed to set global variable " << name; } } } Result GlobalVars::get_named_param_vals( std::vector raw_named_params) const { std::unordered_map> named_params; std::vector unexpected_params; for (const auto ¶m : raw_named_params) { if (param.find("=") != std::string::npos) { auto split = util::split_string(param, '=', /* remove_empty= */ true); named_params[split[0]] = split.size() == 2 ? split[1] : ""; if (!added_global_vars_.contains(split[0])) { unexpected_params.push_back(split[0]); } } else { named_params[param] = std::nullopt; if (!added_global_vars_.contains(param)) { unexpected_params.push_back(param); } } } if (!unexpected_params.empty()) { std::vector expected_params; for (const auto &[name, _] : named_param_defaults_) { expected_params.push_back(name); } return make_error(std::move(unexpected_params), std::move(expected_params)); } GlobalVarMap named_param_vals; for (const auto &[name, default_val] : named_param_defaults_) { const auto &config = added_global_vars_.at(name); auto it = named_params.find(name); // Use the default if (it == named_params.end()) { if (config.type == Type::integer) { named_param_vals[name] = std::get(default_val); } else if (config.type == Type::boolean) { named_param_vals[name] = std::get(default_val); } else if (config.type == Type::string) { named_param_vals[name] = std::get(default_val); } continue; } // No value means it's a boolean if (!it->second.has_value()) { if (config.type == Type::integer) { return make_error(name, "true", "expects an integer"); } else if (config.type == Type::string) { return make_error(name, "true", "expects a string"); } else if (config.type == Type::boolean) { named_param_vals[name] = true; } continue; } const std::string &val = *it->second; if (config.type == Type::integer) { auto int_val = util::to_int(val); if (!int_val) { std::string err_msg; auto ok = handleErrors( std::move(int_val), [&](const util::OverflowError &) { err_msg = "value is out of range"; }, [&](const util::NumberFormatError &err) { err_msg = err.msg(); }); return make_error(name, val, err_msg); } named_param_vals[name] = *int_val; } else if (config.type == Type::boolean) { if (util::is_str_bool_truthy(val)) { named_param_vals[name] = true; } else if (util::is_str_bool_falsy(val)) { named_param_vals[name] = false; } else { return make_error(name, val, "expects a boolean (e.g. 'true')"); } } else if (config.type == Type::string) { named_param_vals[name] = val; } } return named_param_vals; } std::unordered_set GlobalVars::get_global_vars_for_section( std::string_view target_section) { std::unordered_set ret; for (const auto &[name, config] : added_global_vars_) { if (config.section == target_section) { ret.insert(name); } } return ret; } void GlobalVars::update_global_vars( const struct bpf_object *bpf_object, const std::unordered_map §ion_name_to_global_vars_map, GlobalVarMap &&global_var_vals, int ncpus, int max_cpu_id) { global_var_vals[std::string(globalvars::NUM_CPUS)] = ncpus; global_var_vals[std::string(globalvars::MAX_CPU_ID)] = max_cpu_id; verify_maps_found(section_name_to_global_vars_map); 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); if (needed_global_variables.empty()) { continue; } std::map vars_and_offsets = find_btf_var_offsets( bpf_object, section_name, needed_global_variables); if (section_name == RO_SECTION_NAME) { update_global_vars_rodata(global_vars_map, added_global_vars_, vars_and_offsets, global_var_vals); } else { update_global_vars_custom_rw_section(section_name, global_vars_map, vars_and_offsets, needed_global_variables, max_cpu_id); } } } uint64_t *GlobalVars::get_global_var( const struct bpf_object *bpf_object, std::string_view target_section, const std::unordered_map §ion_name_to_global_vars_map) { verify_maps_found(section_name_to_global_vars_map); auto it = std::ranges::find_if(section_name_to_global_vars_map, [target_section](const auto &pair) { return pair.first == target_section; }); if (it == section_name_to_global_vars_map.end()) { LOG(BUG) << target_section << " not found"; } const auto &[section_name, global_vars_map] = *it; const auto needed_global_variables = get_global_vars_for_section( section_name); if (needed_global_variables.empty()) { LOG(BUG) << "No global variables found in section " << section_name; } else if (needed_global_variables.size() > 1) { LOG(BUG) << "Multiple read-write global variables are in same section " << section_name; } auto global_var = *needed_global_variables.begin(); auto vars_and_offsets = find_btf_var_offsets(bpf_object, section_name, needed_global_variables); if (vars_and_offsets.at(global_var) != 0) { LOG(BUG) << "Read-write global variable " << global_var << " must be at offset 0 in section " << section_name; } size_t v_size; auto *target_var = reinterpret_cast( bpf_map__initial_value(global_vars_map, &v_size)); if (!target_var) { LOG(BUG) << "Failed to get array buf for global variable map"; } return target_var; } } // namespace bpftrace::globalvars bpftrace-0.24.1/src/globalvars.h000066400000000000000000000155261506776124200165030ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "types.h" #include "util/result.h" namespace bpftrace { class BPFtrace; class Config; class RequiredResources; namespace globalvars { class NamedParamError : public ErrorInfo { public: NamedParamError(std::string param, std::string value, std::string &&err) : param_(std::move(param)), value_(std::move(value)), err_(std::move(err)) {}; static char ID; void log(llvm::raw_ostream &OS) const override; const std::string &err() const { return err_; } private: std::string param_; std::string value_; std::string err_; }; class UnknownParamError : public ErrorInfo { public: UnknownParamError(std::vector &&unexpected, std::vector &&expected) : unexpected_(std::move(unexpected)), expected_(std::move(expected)) {}; static char ID; void log(llvm::raw_ostream &OS) const override; std::string err() const { std::string err = "unexpected program command line options: "; size_t i; for (i = 0; i < unexpected_.size(); ++i) { err += "--"; err += unexpected_[i]; if (i != unexpected_.size() - 1) { err += ", "; } } return err; } std::string hint() const { std::string hint = "expected program options: "; size_t j; for (j = 0; j < expected_.size(); ++j) { hint += "--"; hint += expected_[j]; if (j != expected_.size() - 1) { hint += ", "; } } return hint; } private: std::vector unexpected_; std::vector expected_; }; using GlobalVarValue = std::variant; using GlobalVarMap = std::unordered_map; // Known global variables constexpr std::string_view NUM_CPUS = "__bt__num_cpus"; constexpr std::string_view MAX_CPU_ID = "__bt__max_cpu_id"; constexpr std::string_view FMT_STRINGS_BUFFER = "__bt__fmt_str_buf"; constexpr std::string_view TUPLE_BUFFER = "__bt__tuple_buf"; constexpr std::string_view GET_STR_BUFFER = "__bt__get_str_buf"; constexpr std::string_view READ_MAP_VALUE_BUFFER = "__bt__read_map_val_buf"; constexpr std::string_view WRITE_MAP_VALUE_BUFFER = "__bt__write_map_val_buf"; constexpr std::string_view VARIABLE_BUFFER = "__bt__var_buf"; constexpr std::string_view MAP_KEY_BUFFER = "__bt__map_key_buf"; constexpr std::string_view EVENT_LOSS_COUNTER = "__bt__event_loss_counter"; // Section names 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"; constexpr std::string_view EVENT_LOSS_COUNTER_SECTION_NAME = ".data.event_loss_counter"; struct GlobalVarConfig { std::string section; Type type = Type::none; private: friend class cereal::access; template void serialize(Archive &archive) { archive(section, type); } }; const std::unordered_map GLOBAL_VAR_CONFIGS = { { NUM_CPUS, { .section = std::string(RO_SECTION_NAME), .type = Type::integer } }, { MAX_CPU_ID, { .section = std::string(RO_SECTION_NAME), .type = Type::integer } }, { EVENT_LOSS_COUNTER, { .section = std::string(EVENT_LOSS_COUNTER_SECTION_NAME), .type = Type::integer } }, { FMT_STRINGS_BUFFER, { .section = std::string(FMT_STRINGS_BUFFER_SECTION_NAME) } }, { TUPLE_BUFFER, { .section = std::string(TUPLE_BUFFER_SECTION_NAME) } }, { GET_STR_BUFFER, { .section = std::string(GET_STR_BUFFER_SECTION_NAME) } }, { READ_MAP_VALUE_BUFFER, { .section = std::string(READ_MAP_VALUE_BUFFER_SECTION_NAME) } }, { WRITE_MAP_VALUE_BUFFER, { .section = std::string(WRITE_MAP_VALUE_BUFFER_SECTION_NAME) } }, { VARIABLE_BUFFER, { .section = std::string(VARIABLE_BUFFER_SECTION_NAME) } }, { MAP_KEY_BUFFER, { .section = std::string(MAP_KEY_BUFFER_SECTION_NAME) } }, }; class GlobalVars { public: GlobalVars() = default; GlobalVars(std::unordered_map global_var_map, std::unordered_map default_values) : added_global_vars_(std::move(global_var_map)), named_param_defaults_(std::move(default_values)) { } void add_known(const std::string_view &name); void add_named_param(const std::string &name, const GlobalVarValue &default_value); Result get_named_param_vals( std::vector raw_named_params) const; const GlobalVarConfig &get_config(const std::string &name) const; SizedType get_sized_type(const std::string &global_var_name, const RequiredResources &resources, const Config &bpftrace_config) const; void check_index(const std::string &global_var_name, const RequiredResources &resources, size_t index) const; const std::unordered_map &global_var_map() const { return added_global_vars_; } void update_global_vars( const struct bpf_object *bpf_object, const std::unordered_map &global_vars_map, GlobalVarMap &&global_var_vals, int ncpus, int max_cpu_id); std::unordered_set get_global_vars_for_section( std::string_view target_section); uint64_t *get_global_var( const struct bpf_object *bpf_object, std::string_view target_section, const std::unordered_map §ion_name_to_global_vars_map); protected: std::unordered_map added_global_vars_; std::unordered_map named_param_defaults_; private: friend class cereal::access; template void serialize(Archive &archive) { archive(added_global_vars_, named_param_defaults_); } void verify_maps_found(const std::unordered_map §ion_name_to_global_vars_map); }; SizedType get_type(const std::string &global_var_name, const RequiredResources &resources, const Config &bpftrace_config); std::unordered_set get_section_names(); } // namespace globalvars } // namespace bpftrace bpftrace-0.24.1/src/kfuncs.h000066400000000000000000000007041506776124200156300ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace { enum class Kfunc { bpf_map_sum_elem_count, bpf_session_is_return, }; static const std::map KFUNC_NAME_MAP = { { Kfunc::bpf_map_sum_elem_count, "bpf_map_sum_elem_count" }, { Kfunc::bpf_session_is_return, "bpf_session_is_return" }, }; inline const std::string &kfunc_name(enum Kfunc kfunc) { return KFUNC_NAME_MAP.at(kfunc); } } // namespace bpftrace bpftrace-0.24.1/src/ksyms.cpp000066400000000000000000000114061506776124200160410ustar00rootroot00000000000000#include #include #include "ksyms.h" #include "scopeguard.h" namespace { std::string stringify_addr(uint64_t addr) { std::ostringstream symbol; symbol << reinterpret_cast(addr); return symbol.str(); } #ifdef HAVE_BLAZESYM std::string stringify_ksym(const char *name, const blaze_symbolize_code_info *code_info, uint64_t offset, bool show_offset, bool perf_mode, bool is_inlined) { std::ostringstream symbol; if (is_inlined && !perf_mode) { symbol << "[inlined] "; } symbol << name; if (show_offset) { symbol << "+" << offset; } if (perf_mode) { if (is_inlined) { symbol << " (inlined)"; } return symbol.str(); } if (code_info != nullptr) { if (code_info->dir != nullptr && code_info->file != nullptr) { symbol << "@" << code_info->dir << "/" << code_info->file << ":" << code_info->line; } else if (code_info->file != nullptr) { symbol << "@" << code_info->file << ":" << code_info->line; } } return symbol.str(); } #endif } // 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::vector Ksyms::resolve_blazesym_impl(uint64_t addr, bool show_offset, bool perf_mode, bool show_debug_info) { std::vector str_syms; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" if (symbolizer_ == nullptr) { blaze_symbolizer_opts opts = { .type_size = sizeof(opts), // Use the config here because the symbolizer is created once .code_info = config_.show_debug_info, .inlined_fns = config_.show_debug_info, }; symbolizer_ = blaze_symbolizer_new_opts(&opts); if (symbolizer_ == nullptr) return str_syms; } 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 = "", .debug_syms = show_debug_info, }; #pragma GCC diagnostic pop const blaze_syms *syms = blaze_symbolize_kernel_abs_addrs( symbolizer_, &src, &addr, 1); if (syms == nullptr) return str_syms; SCOPE_EXIT { blaze_syms_free(syms); }; const blaze_sym *sym = &syms->syms[0]; const struct blaze_symbolize_inlined_fn *inlined; if (sym == nullptr || sym->name == nullptr) { return str_syms; } // bpftrace prints stacks leaf first so the inlined functions // need to come first in the list (and in reverse order) for (int j = static_cast(sym->inlined_cnt) - 1; j >= 0; j--) { inlined = &sym->inlined[j]; if (inlined != nullptr) { str_syms.push_back(stringify_ksym( inlined->name, &inlined->code_info, 0, false, perf_mode, true)); } } str_syms.push_back(stringify_ksym( sym->name, &sym->code_info, sym->offset, show_offset, perf_mode, false)); return str_syms; } std::vector Ksyms::resolve_blazesym(uint64_t addr, bool show_offset, bool perf_mode, bool show_debug_info) { auto syms = resolve_blazesym_impl( addr, show_offset, perf_mode, show_debug_info); if (syms.empty()) { syms.push_back(stringify_addr(addr)); } return syms; } #endif std::vector Ksyms::resolve(uint64_t addr, bool show_offset, [[maybe_unused]] bool perf_mode, [[maybe_unused]] bool show_debug_info) { #ifdef HAVE_BLAZESYM if (config_.use_blazesym) return resolve_blazesym(addr, show_offset, perf_mode, show_debug_info); #endif return std::vector{ resolve_bcc(addr, show_offset) }; } } // namespace bpftrace bpftrace-0.24.1/src/ksyms.h000066400000000000000000000023651506776124200155120ustar00rootroot00000000000000#pragma once #include #include #include #ifdef HAVE_BLAZESYM #include #endif #include "config.h" namespace bpftrace { class Config; class Ksyms { public: Ksyms(const Config &config); ~Ksyms(); Ksyms(Ksyms &) = delete; Ksyms &operator=(const Ksyms &) = delete; std::vector resolve(uint64_t addr, bool show_offset, bool perf_mode, bool show_debug_info); private: const Config &config_; void *ksyms_{ nullptr }; #ifdef HAVE_BLAZESYM blaze_symbolizer *symbolizer_{ nullptr }; std::vector resolve_blazesym_impl(uint64_t addr, bool show_offset, bool perf_mode, bool show_debug_info); std::vector resolve_blazesym(uint64_t addr, bool show_offset, bool perf_mode, bool show_debug_info); #endif std::string resolve_bcc(uint64_t addr, bool show_offset); }; } // namespace bpftrace bpftrace-0.24.1/src/lexer.l000066400000000000000000000344141506776124200154670ustar00rootroot00000000000000%option yylineno noyywrap noinput %option never-interactive %option reentrant %option stack %{ #include #include "driver.h" #include "parser.tab.hh" #include "util/int_parser.h" #include "util/strings.h" #define YY_USER_ACTION driver.loc.columns(yyleng); #define yyterminate() return bpftrace::Parser::make_END(driver.loc) using namespace bpftrace; // Since `YY_INPUT` cannot access the `driver` variable, `source` and `curr` // are defined as global variables. They are marked as thread_local to // ensure thread safety during lexical analysis. static thread_local const std::string *source; static thread_local size_t curr; void set_source_string(const std::string *s); static int read_from_source(char* buf, size_t max_size); #define YY_INPUT(buf,result,max_size) \ result = read_from_source(buf, max_size); %} /* https://en.cppreference.com/w/cpp/language/integer_literal#The_type_of_the_literal */ int_size (([uU])|([uU]?[lL]?[lL])|(ns)|(us)|(ms)|(s)|(m)|(h)|(d)) /* Number with underscores in it, e.g. 1_000_000 */ int [0-9]([0-9_]*[0-9])?{int_size}? bool true|false 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#$+\*])+ /* Most of the builtins are prefixed with __builtin_ as they are exposed to users via macros e.g. macro cpu() { __builtin_cpu } */ builtin arg[0-9]+|args|ctx|kstack|nsecs|pid|sarg[0-9]|tid|ustack|__builtin_cgroup|__builtin_comm|__builtin_cpid|__builtin_cpu|__builtin_curtask|__builtin_elapsed|__builtin_func|__builtin_gid|__builtin_jiffies|__builtin_ncpus|__builtin_numaid|__builtin_probe|__builtin_rand|__builtin_retval|__builtin_uid|__builtin_usermode|__builtin_username int_type bool|(u)?int(8|16|32|64) builtin_type void|(u)?(min|max|sum|avg|stats)_t|count_t|probe_t|username_t|lhist_t|hist_t|usym_t|ksym_t|timestamp|macaddr_t|cgroup_path_t|strerror_t|kstack_t|ustack_t|string|tseries_t sized_type inet|buffer subprog fn macro macro /* 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 %% %{ if (driver.token) { auto t = *driver.token; driver.token.reset(); return t; } %} {hspace}+ { driver.loc.step(); } {vspace}+ { driver.loc.lines(yyleng); driver.loc.step(); } ^"#!".*$ // executable line "//".*$ // single-line comments "/*" yy_push_state(COMMENT, yyscanner); { "*/" yy_pop_state(yyscanner); [^*\n]+|"*" {} \n driver.loc.lines(1); driver.loc.step(); <> yy_pop_state(yyscanner); driver.error(driver.loc, "end of file during comment"); } {builtin} { return Parser::make_BUILTIN(yytext, driver.loc); } {subprog} { return Parser::make_SUBPROG(yytext, driver.loc); } {macro} { return Parser::make_MACRO(yytext, driver.loc); } {int}|{hex}|{exponent} { // Note that we have no unsigned integers in the lexer, these // are purely derived as a result of folding constants. auto res = util::to_uint(yytext, 0); if (!res) { std::stringstream ss; ss << res.takeError(); driver.error(driver.loc, ss.str()); } else { return Parser::make_UNSIGNED_INT(*res, driver.loc); } } {bool} { if (std::string(yytext) == "true") { return Parser::make_BOOL(true, driver.loc); } return Parser::make_BOOL(false, driver.loc); } {path} { return Parser::make_PATH(yytext, driver.loc); } {map} { return Parser::make_MAP(yytext, driver.loc); } {var} { return Parser::make_VAR(yytext, driver.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(driver.loc); } ";" { return Parser::make_SEMI(driver.loc); } "{" { return Parser::make_LBRACE(driver.loc); } "}" { return Parser::make_RBRACE(driver.loc); } "[" { return Parser::make_LBRACKET(driver.loc); } "]" { return Parser::make_RBRACKET(driver.loc); } "(" { return Parser::make_LPAREN(driver.loc); } ")" { return Parser::make_RPAREN(driver.loc); } \//{space}*[\/\{] { return Parser::make_ENDPRED(driver.loc); } /* If "/" is followed by "/" or "{", choose ENDPRED, otherwise DIV */ "," { return Parser::make_COMMA(driver.loc); } "=" { return Parser::make_ASSIGN(driver.loc); } "<<=" { return Parser::make_LEFTASSIGN(driver.loc); } ">>=" { return Parser::make_RIGHTASSIGN(driver.loc); } "+=" { return Parser::make_PLUSASSIGN(driver.loc); } "-=" { return Parser::make_MINUSASSIGN(driver.loc); } "*=" { return Parser::make_MULASSIGN(driver.loc); } "/=" { return Parser::make_DIVASSIGN(driver.loc); } "%=" { return Parser::make_MODASSIGN(driver.loc); } "&=" { return Parser::make_BANDASSIGN(driver.loc); } "|=" { return Parser::make_BORASSIGN(driver.loc); } "^=" { return Parser::make_BXORASSIGN(driver.loc); } "==" { return Parser::make_EQ(driver.loc); } "!=" { return Parser::make_NE(driver.loc); } "<=" { return Parser::make_LE(driver.loc); } ">=" { return Parser::make_GE(driver.loc); } "<<" { return Parser::make_LEFT(driver.loc); } ">>" { return Parser::make_RIGHT(driver.loc); } "<" { return Parser::make_LT(driver.loc); } ">" { return Parser::make_GT(driver.loc); } "&&" { return Parser::make_LAND(driver.loc); } "||" { return Parser::make_LOR(driver.loc); } "+" { return Parser::make_PLUS(driver.loc); } "-" { return Parser::make_MINUS(driver.loc); } "++" { return Parser::make_INCREMENT(driver.loc); } "--" { return Parser::make_DECREMENT(driver.loc); } "*" { return Parser::make_MUL(driver.loc); } "/" { return Parser::make_DIV(driver.loc); } "%" { return Parser::make_MOD(driver.loc); } "&" { return Parser::make_BAND(driver.loc); } "|" { return Parser::make_BOR(driver.loc); } "^" { return Parser::make_BXOR(driver.loc); } "!" { return Parser::make_LNOT(driver.loc); } "~" { return Parser::make_BNOT(driver.loc); } "." { return Parser::make_DOT(driver.loc); } "->" { return Parser::make_PTR(driver.loc); } "$"[0-9]+ { return Parser::make_PARAM(yytext, driver.loc); } "$"# { return Parser::make_PARAMCOUNT(driver.loc); } "#"[^!].* { return Parser::make_CPREPROC(yytext, driver.loc); } "if" { return Parser::make_IF(yytext, driver.loc); } "else" { return Parser::make_ELSE(yytext, driver.loc); } "?" { return Parser::make_QUES(driver.loc); } "unroll" { return Parser::make_UNROLL(yytext, driver.loc); } "while" { return Parser::make_WHILE(yytext, driver.loc); } "config" { return Parser::make_CONFIG(yytext, driver.loc); } "for" { return Parser::make_FOR(yytext, driver.loc); } "return" { return Parser::make_RETURN(yytext, driver.loc); } "continue" { return Parser::make_CONTINUE(yytext, driver.loc); } "break" { return Parser::make_BREAK(yytext, driver.loc); } "sizeof" { return Parser::make_SIZEOF(yytext, driver.loc); } "offsetof" { return Parser::make_OFFSETOF(yytext, driver.loc); } "let" { return Parser::make_LET(yytext, driver.loc); } "import" { return Parser::make_IMPORT(yytext, driver.loc); } {int_type} { return Parser::make_INT_TYPE(yytext, driver.loc); } {builtin_type} { return Parser::make_BUILTIN_TYPE(yytext, driver.loc); } {sized_type} { return Parser::make_SIZED_TYPE(yytext, driver.loc); } \" { yy_push_state(STR, yyscanner); driver.buffer.clear(); } { \" { yy_pop_state(yyscanner); return Parser::make_STRING(driver.buffer, driver.loc); } [^\\\n\"]+ driver.buffer += yytext; \\n driver.buffer += '\n'; \\t driver.buffer += '\t'; \\r driver.buffer += '\r'; \\\" driver.buffer += '\"'; \\\\ driver.buffer += '\\'; \\{oct_esc} { long value = strtol(yytext+1, NULL, 8); if (value > UCHAR_MAX) driver.error(driver.loc, std::string("octal escape sequence out of range '") + yytext + "'"); driver.buffer += value; } \\{hex_esc} driver.buffer += strtol(yytext+2, NULL, 16); \n driver.error(driver.loc, "unterminated string"); yy_pop_state(yyscanner); driver.loc.lines(1); driver.loc.step(); <> driver.error(driver.loc, "unterminated string"); yy_pop_state(yyscanner); \\. { driver.error(driver.loc, std::string("invalid escape character '") + yytext + "'"); } . driver.error(driver.loc, "invalid character"); yy_pop_state(yyscanner); } struct|union|enum { yy_push_state(STRUCT, yyscanner); driver.buffer.clear(); driver.struct_type = yytext; return Parser::make_STRUCT(driver.loc); } { {hspace}+ { driver.loc.step(); } {vspace}+ { driver.loc.lines(yyleng); driver.loc.step(); } struct|union|enum { yy_pop_state(yyscanner); yy_push_state(STRUCT_AFTER_COLON, yyscanner); driver.buffer.clear(); driver.struct_type = yytext; return Parser::make_STRUCT(driver.loc); } . { unput(yytext[0]); yy_pop_state(yyscanner); } } { {hspace}+ { driver.loc.step(); } {vspace}+ { driver.loc.lines(yyleng); driver.loc.step(); } {ident} { driver.buffer = yytext; yy_pop_state(yyscanner); return Parser::make_IDENT(driver.struct_type + " " + util::trim(driver.buffer), driver.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(driver.struct_type + " " + util::trim(driver.buffer), driver.loc); } driver.buffer += yytext[0]; } "{" yy_push_state(BRACE, yyscanner); driver.buffer += '{'; "}"|"};" { driver.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(driver.struct_type + driver.buffer, driver.loc); } } . driver.buffer += yytext[0]; \n driver.buffer += '\n'; driver.loc.lines(1); driver.loc.step(); } {ident} { return Parser::make_IDENT(yytext, driver.loc); } . { driver.error(driver.loc, std::string("invalid character '") + std::string(yytext) + std::string("'")); } %% void set_source_string(const std::string *s) { source = s; curr = 0; } // Here we replaced the original YY_INPUT with the read_from_source() function, // allowing flex to read the source code from a string rather than a file. In // this case, flex uses a buffer size of YY_BUF_SIZE (16384) and reads up to // YY_READ_BUF_SIZE (8192) at a time. This gives us enough space for macro // expansion. Additionally, just like reading from a file, flex's internal // buffer management can handle cases where the string size exceeds YY_BUF_SIZE. int read_from_source(char* buf, size_t max_size) { size_t num_to_copy = std::min(source->size() - curr, max_size); source->copy(buf, num_to_copy, curr); curr += num_to_copy; return num_to_copy; } bpftrace-0.24.1/src/libbpf/000077500000000000000000000000001506776124200154235ustar00rootroot00000000000000bpftrace-0.24.1/src/libbpf/bpf.h000066400000000000000000000224111506776124200163430ustar00rootroot00000000000000#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, BPF_CGROUP_UNIX_CONNECT, BPF_CGROUP_UNIX_SENDMSG, BPF_CGROUP_UNIX_RECVMSG, BPF_CGROUP_UNIX_GETPEERNAME, BPF_CGROUP_UNIX_GETSOCKNAME, BPF_NETKIT_PRIMARY, BPF_NETKIT_PEER, BPF_TRACE_KPROBE_SESSION, BPF_TRACE_UPROBE_SESSION, }; #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.24.1/src/lockdown.cpp000066400000000000000000000030451506776124200165130ustar00rootroot00000000000000#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.24.1/src/lockdown.h000066400000000000000000000004611506776124200161570ustar00rootroot00000000000000#pragma once #include namespace bpftrace::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 bpftrace::lockdown bpftrace-0.24.1/src/log.cpp000066400000000000000000000072341506776124200154600ustar00rootroot00000000000000#include "log.h" #include namespace bpftrace { static 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, std::optional&& source_location, std::optional>&& source_context, std::ostream& out, std::string&& msg) { if (!msg.empty() && msg.back() == '\n') { msg.pop_back(); } const char* color_begin = LogColor::DEFAULT; const char* color_end = LogColor::DEFAULT; if (is_colorize_) { switch (type) { case LogType::ERROR: case LogType::BUG: 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 (source_location) { out << *source_location << ": "; } const std::string& typestr = logtype_str(type); out << typestr << msg << color_end << std::endl; if (source_context) { for (const auto& s : *source_context) { out << s << std::endl; } } } LogStream::LogStream(const std::string& file, int line, LogType type, std::ostream& out) : file_(file), line_(line), type_(type), out_(out) { } LogStream::LogStream(const std::string& file, int line, LogType type, std::string&& source_location, std::ostream& out) : file_(file), line_(line), type_(type), source_location_(std::move(source_location)), out_(out) { } LogStream::LogStream(const std::string& file, int line, LogType type, std::string&& source_location, std::vector&& source_context, std::ostream& out) : file_(file), line_(line), type_(type), source_location_(std::move(source_location)), source_context_(std::move(source_context)), out_(out) { } LogStream::~LogStream() { auto& sink = Log::get(); if (sink.is_enabled(type_)) { auto msg = buf_.str(); if (type_ == LogType::DEBUG) msg = internal_location() + msg; // Pass ownership of all the things to the sink itself, which will evaluate // what's available and what's not. sink.take_input(type_, std::move(source_location_), std::move(source_context_), out_, std::move(msg)); } } std::string LogStream::internal_location() { std::ostringstream ss; ss << "[" << file_ << ":" << line_ << "] "; return ss.str(); } [[noreturn]] LogStreamBug::~LogStreamBug() { auto& sink = Log::get(); sink.take_input(type_, std::nullopt, std::nullopt, out_, internal_location() + buf_.str()); abort(); } }; // namespace bpftrace bpftrace-0.24.1/src/log.h000066400000000000000000000077011506776124200151240ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include 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: Log(const Log& other) = delete; Log& operator=(const Log& other) = delete; Log(Log&& other) = delete; Log& operator=(Log&& other) = delete; static Log& get(); void take_input(LogType type, std::optional&& source_location, std::optional>&& source_context, std::ostream& out, std::string&& msg); void enable(LogType type) { enabled_map_[type] = true; } void disable(LogType type) { assert(type != LogType::BUG && type != LogType::ERROR); enabled_map_[type] = false; } bool is_enabled(LogType type) { return enabled_map_[type]; } void set_colorize(bool is_colorize) { is_colorize_ = is_colorize; } private: Log(); ~Log() = default; 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::string&& source_location, std::ostream& out = std::cerr); LogStream(const std::string& file, int line, LogType type, std::string&& source_location, std::vector&& source_context, std::ostream& out = std::cerr); template LogStream& operator<<(const T& v) { auto& sink = Log::get(); if (sink.is_enabled(type_)) buf_ << v; return *this; } virtual ~LogStream(); protected: // This formats the `file_` and `line_` and may be used to prefix the message // for some types of log streams. std::string internal_location(); const std::string& file_; const int line_; LogType type_; std::optional source_location_; std::optional> source_context_; std::ostream& out_; std::ostringstream buf_; }; class LogStreamBug : public LogStream { public: LogStreamBug(const std::string& file, int line, __attribute__((unused)) LogType /*unused*/, std::ostream& out = std::cerr) : LogStream(file, line, LogType::BUG, out) {}; [[noreturn]] ~LogStreamBug() override; }; // clang-format off // 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.source_location(), std::cerr) << "error with location"; // 4. LOG(ERROR, call.loc.source_location(), call.loc.source_context(), std::err) << "error with context"; // // Note: LogType::DEBUG will prepend __FILE__ and __LINE__ to the debug message. #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.24.1/src/main.cpp000066400000000000000000001026251506776124200156230ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aot/aot.h" #include "ast/attachpoint_parser.h" #include "ast/diagnostic.h" #include "ast/helpers.h" #include "ast/pass_manager.h" #include "ast/passes/clang_build.h" #include "ast/passes/clang_parser.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/fold_literals.h" #include "ast/passes/map_sugar.h" #include "ast/passes/named_param.h" #include "ast/passes/parser.h" #include "ast/passes/pid_filter_pass.h" #include "ast/passes/portability_analyser.h" #include "ast/passes/printer.h" #include "ast/passes/probe_prune.h" #include "ast/passes/recursion_check.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/return_path_analyser.h" #include "ast/passes/semantic_analyser.h" #include "ast/passes/type_system.h" #include "benchmark.h" #include "bpffeature.h" #include "bpftrace.h" #include "btf.h" #include "build_info.h" #include "child.h" #include "config.h" #include "globalvars.h" #include "lockdown.h" #include "log.h" #include "probe_matcher.h" #include "procmon.h" #include "run_bpftrace.h" #include "util/env.h" #include "util/int_parser.h" #include "util/kernel.h" #include "util/strings.h" #include "version.h" using namespace bpftrace; namespace { enum class OutputBufferConfig { UNSET = 0, LINE, FULL, NONE, }; enum class TestMode { NONE = 0, CODEGEN, COMPILER_BENCHMARK, BPF_BENCHMARK, }; 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_MODE, AOT, HELP, VERSION, USDT_SEMAPHORE, UNSAFE, BTF, INCLUDE, EMIT_ELF, EMIT_LLVM, NO_FEATURE, DEBUG, DRY_RUN, VERIFY_LLVM_IR, }; constexpr auto FULL_SEARCH = "*:*"; } // 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 filter actions and enable USDT probes on PID" << std::endl; out << " -c 'CMD' run CMD and enable USDT probes on resulting process" << std::endl; out << " --no-feature FEATURE[,FEATURE]" << std::endl; out << " disable use of detected features" << 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 << " --verify-llvm-ir check that the generated LLVM IR is valid" << 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); auto btf = bpftrace::BTF(); 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, btf).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); } std::vector extra_flags( BPFtrace& bpftrace, const std::vector& include_dirs, const std::vector& include_files) { std::string ksrc, kobj; struct utsname utsname; std::vector extra_flags; uname(&utsname); bool found_kernel_headers = util::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.emplace_back("-I"); extra_flags.push_back(dir); } for (auto file : include_files) { extra_flags.emplace_back("-include"); extra_flags.push_back(file); } return extra_flags; } 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; bool verify_llvm_ir = false; TestMode test_mode = TestMode::NONE; 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; std::vector named_params; }; void CreateDynamicPasses(std::function add) { add(ast::CreateFoldLiteralsPass()); add(ast::CreatePidFilterPass()); add(ast::CreateClangBuildPass()); add(ast::CreateTypeSystemPass()); add(ast::CreateSemanticPass()); add(ast::CreateProbePrunePass()); add(ast::CreateResourcePass()); add(ast::CreateRecursionCheckPass()); add(ast::CreateReturnPathPass()); } void CreateAotPasses(std::function add) { add(ast::CreatePortabilityPass()); add(ast::CreateFoldLiteralsPass()); add(ast::CreateClangBuildPass()); add(ast::CreateTypeSystemPass()); add(ast::CreateSemanticPass()); add(ast::CreateProbePrunePass()); add(ast::CreateResourcePass()); add(ast::CreateRecursionCheckPass()); add(ast::CreateReturnPathPass()); } ast::Pass printPass(const std::string& name) { return ast::Pass::create("print-" + name, [=](ast::ASTContext& ast) { std::cerr << "AST after: " << name << std::endl; std::cerr << "-------------------" << std::endl; ast::Printer printer(std::cerr); printer.visit(ast.root); std::cerr << std::endl; }); }; static bool parse_debug_stages(const std::string& arg) { auto stages = util::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{ .name = "help", .has_arg = no_argument, .flag = nullptr, .val = Options::HELP }, option{ .name = "version", .has_arg = no_argument, .flag = nullptr, .val = Options::VERSION }, option{ .name = "usdt-file-activation", .has_arg = no_argument, .flag = nullptr, .val = Options::USDT_SEMAPHORE }, option{ .name = "unsafe", .has_arg = no_argument, .flag = nullptr, .val = Options::UNSAFE }, option{ .name = "btf", .has_arg = no_argument, .flag = nullptr, .val = Options::BTF }, option{ .name = "include", .has_arg = required_argument, .flag = nullptr, .val = Options::INCLUDE }, option{ .name = "info", .has_arg = no_argument, .flag = nullptr, .val = Options::INFO }, option{ .name = "emit-llvm", .has_arg = required_argument, .flag = nullptr, .val = Options::EMIT_LLVM }, option{ .name = "emit-elf", .has_arg = required_argument, .flag = nullptr, .val = Options::EMIT_ELF }, option{ .name = "no-warnings", .has_arg = no_argument, .flag = nullptr, .val = Options::NO_WARNING }, option{ .name = "test-mode", .has_arg = required_argument, .flag = nullptr, .val = Options::TEST_MODE }, option{ .name = "aot", .has_arg = required_argument, .flag = nullptr, .val = Options::AOT }, option{ .name = "no-feature", .has_arg = required_argument, .flag = nullptr, .val = Options::NO_FEATURE }, option{ .name = "debug", .has_arg = required_argument, .flag = nullptr, .val = Options::DEBUG }, option{ .name = "dry-run", .has_arg = no_argument, .flag = nullptr, .val = Options::DRY_RUN }, option{ .name = "verify-llvm-ir", .has_arg = no_argument, .flag = nullptr, .val = Options::VERIFY_LLVM_IR }, option{ .name = nullptr, .has_arg = 0, .flag = nullptr, .val = 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_MODE: // --test-mode if (std::strcmp(optarg, "codegen") == 0) { args.test_mode = TestMode::CODEGEN; } else if (std::strcmp(optarg, "compiler-bench") == 0) { args.test_mode = TestMode::COMPILER_BENCHMARK; } else if (std::strcmp(optarg, "bench") == 0) { args.test_mode = TestMode::BPF_BENCHMARK; } else { LOG(ERROR) << "USAGE: --test can only be 'codegen', " "'compiler-bench', or 'bench'."; 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,kprobe_session,uprobe_multi'."; exit(1); } break; case Options::DRY_RUN: dry_run = true; break; case Options::VERIFY_LLVM_IR: args.verify_llvm_ir = 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.emplace_back(optarg); break; case Options::INCLUDE: args.include_files.emplace_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 = FULL_SEARCH; } else if (optind == argc - 1) { std::string val(argv[optind]); if (std::filesystem::exists(val)) { args.filename = val; } else { if (val == "*") { args.search = FULL_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++; } // Parse positional and named parameters. while (optind < argc) { auto pos_arg = std::string(argv[optind]); if (pos_arg.starts_with("--")) { args.named_params.emplace_back(pos_arg.substr(2)); } else { args.params.emplace_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); } } static ast::ASTContext buildListProgram(const std::string& search) { ast::ASTContext ast("listing", search); auto* ap = ast.make_node(search, true, location()); auto* probe = ast.make_node( ast::AttachPointList({ ap }), nullptr, nullptr, location()); ast.root = ast.make_node("", nullptr, ast::ImportList(), ast::RootStatements({ probe }), location()); return ast; } int main(int argc, char* argv[]) { Log::get().set_colorize(is_colorize()); Args args = parse_args(argc, argv); 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); auto config = std::make_unique(!args.cmd_str.empty()); BPFtrace bpftrace(args.no_feature, std::move(config)); // Most configuration can be applied during the configuration pass, however // we need to extract a few bits of configuration up front, because they may // affect the actual compilation process. util::get_uint64_env_var("BPFTRACE_MAX_AST_NODES", [&](uint64_t x) { bpftrace.max_ast_nodes_ = x; }); util::get_bool_env_var("BPFTRACE_DEBUG_OUTPUT", [&](bool x) { bpftrace.debug_output_ = x; }); 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(); bpftrace.run_benchmarks_ = args.test_mode == TestMode::BPF_BENCHMARK; if (!args.pid_str.empty()) { auto maybe_pid = util::to_uint(args.pid_str); if (!maybe_pid) { LOG(ERROR) << "Failed to parse pid: " << maybe_pid.takeError(); exit(1); } if (*maybe_pid > 0x400000) { // The actual maximum pid depends on the configuration for the specific // system, i.e. read from `/proc/sys/kernel/pid_max`. We can impose a // basic sanity check here against the nominal maximum for 64-bit // systems. LOG(ERROR) << "Pid out of range: " << *maybe_pid; 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); } } // This is our primary program AST context. Initially it is empty, i.e. // there is no filename set or source file. The way we set it up depends on // the mode of execution below, and we expect that it will be reinitialized. ast::ASTContext ast; // Listing probes when there is no program. if (args.listing && args.script.empty() && args.filename.empty()) { check_is_root(); 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."; } bool is_search_a_type = is_type_name(args.search); // To list tracepoints, we construct a synthetic AST and then expand the // probe. The raw contents of the program are the initial search provided. ast = buildListProgram(is_search_a_type ? FULL_SEARCH : args.search); ast::CDefinitions no_c_defs; // No external C definitions may be used. ast::TypeMetadata no_types; // No external types may be used. // Parse and expand all the attachpoints. We don't need to descend into // the actual driver here, since we know that the program is already // formed. auto pmresult = ast::PassManager() .put(ast) .put(bpftrace) .put(no_c_defs) .put(no_types) .add(ast::CreateParseAttachpointsPass(args.listing)) .add(CreateParseBTFPass()) .add(ast::CreateMapSugarPass()) .add(ast::CreateNamedParamsPass()) .add(ast::CreateSemanticPass(args.listing)) .run(); if (!pmresult) { std::cerr << pmresult.takeError() << "\n"; return 2; } else if (!ast.diagnostics().ok()) { ast.diagnostics().emit(std::cerr); return 1; } if (is_search_a_type) { bpftrace.probe_matcher_->list_structs(args.search); } else { bpftrace.probe_matcher_->list_probes(ast.root); } return 0; } 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; } ast = ast::ASTContext("stdin", buf.str()); } else { std::ifstream file(args.filename); if (file.fail()) { LOG(ERROR) << "failed to open file '" << args.filename << "': " << std::strerror(errno); exit(1); } buf << file.rdbuf(); ast = ast::ASTContext(args.filename, buf.str()); } } else { // Script is provided as a command line argument. ast = ast::ASTContext("stdin", args.script); } for (const auto& param : args.params) { bpftrace.add_param(param); } // If we are not running anything, then we don't require root. if (args.test_mode == TestMode::NONE) { 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(); } // Temporarily, we make the full `BPFTrace` object available via the pass // manager (and objects are temporarily mutable). As passes are refactored // into lighter-weight components, the `BPFTrace` object should be // decomposed into its meaningful parts. Furthermore, the codegen and field // analysis passes will be rolled into the pass manager as regular passes; // the final binary is merely one of the outputs that can be extracted. ast::PassManager pm; pm.put(ast); pm.put(bpftrace); auto flags = extra_flags(bpftrace, args.include_dirs, args.include_files); if (args.listing) { ast::CDefinitions no_c_defs; // See above. ast::TypeMetadata no_types; // See above. pm.add(CreateParsePass(bpftrace.max_ast_nodes_, bt_debug.contains(DebugStage::Parse))) .put(no_c_defs) .put(no_types) .add(ast::CreateParseAttachpointsPass(args.listing)) .add(CreateParseBTFPass()) .add(ast::CreateMapSugarPass()) .add(ast::CreateNamedParamsPass()) .add(ast::CreateSemanticPass(args.listing)); auto pmresult = pm.run(); if (!pmresult) { std::cerr << pmresult.takeError() << "\n"; return 2; } else if (!ast.diagnostics().ok()) { ast.diagnostics().emit(std::cerr); return 1; } bpftrace.probe_matcher_->list_probes(ast.root); return 0; } // Wrap all added passes in passes that dump the intermediate state. These // could dump intermediate objects from the context as well, but preserve // existing behavior for now. auto addPass = [&pm](ast::Pass&& pass) { auto name = pass.name(); pm.add(std::move(pass)); if (bt_debug.contains(DebugStage::Ast)) { pm.add(printPass(name)); } }; // Start with all the basic parsing steps. for (auto& pass : ast::AllParsePasses(std::move(flags), {}, bt_debug.contains(DebugStage::Parse))) { addPass(std::move(pass)); } pm.add(ast::CreateLLVMInitPass()); switch (args.build_mode) { case BuildMode::DYNAMIC: CreateDynamicPasses(addPass); break; case BuildMode::AHEAD_OF_TIME: CreateAotPasses(addPass); break; } if (bt_debug.contains(DebugStage::Types)) { pm.add(ast::CreateDumpTypesPass(std::cout)); } pm.add(ast::CreateCompilePass()); pm.add(ast::CreateLinkBitcodePass()); if (bt_debug.contains(DebugStage::Codegen)) { pm.add(ast::Pass::create("dump-ir-prefix", [&] { std::cout << "LLVM IR before optimization\n"; std::cout << "---------------------------\n\n"; })); pm.add(ast::CreateDumpIRPass(std::cout)); } std::optional output_ir; if (!args.output_llvm.empty()) { output_ir = std::ofstream(args.output_llvm + ".original.ll"); pm.add(ast::CreateDumpIRPass(*output_ir)); } if (args.verify_llvm_ir) { pm.add(ast::CreateVerifyPass()); } pm.add(ast::CreateOptimizePass()); if (bt_debug.contains(DebugStage::CodegenOpt)) { pm.add(ast::Pass::create("dump-ir-opt-prefix", [&] { std::cout << "\nLLVM IR after optimization\n"; std::cout << "----------------------------\n\n"; })); pm.add(ast::CreateDumpIRPass(std::cout)); } std::optional output_ir_opt; if (!args.output_llvm.empty()) { output_ir_opt = std::ofstream(args.output_llvm + ".optimized.ll"); pm.add(ast::CreateDumpIRPass(*output_ir_opt)); } pm.add(ast::CreateObjectPass()); if (bt_debug.contains(DebugStage::Disassemble)) { pm.add(ast::Pass::create("dump-asm-prefix", [&] { std::cout << "\nDisassembled bytecode\n"; std::cout << "----------------------------\n\n"; })); pm.add(ast::CreateDumpASMPass(std::cout)); } if (!args.output_elf.empty()) { pm.add(ast::Pass::create("dump-elf", [&](ast::BpfObject& obj) { std::ofstream out(args.output_elf); out.write(obj.data.data(), obj.data.size()); })); } pm.add(ast::CreateExternObjectPass()); pm.add(ast::CreateLinkPass()); if (args.test_mode == TestMode::COMPILER_BENCHMARK) { info(args.no_feature); auto ok = benchmark(std::cout, pm); if (!ok) { std::cerr << "Benchmark error: " << ok.takeError(); return 1; } return 0; } auto pmresult = pm.run(); if (!pmresult) { std::cerr << pmresult.takeError() << "\n"; return 2; } else if (!ast.diagnostics().ok()) { ast.diagnostics().emit(std::cerr); return 1; } // Emits warnings ast.diagnostics().emit(std::cout); if (args.build_mode == BuildMode::AHEAD_OF_TIME) { // Note: this should use the fully-linked version in the future, but // presently it is just using the single object. auto& out = pmresult->get(); return aot::generate( bpftrace.resources, args.aot, out.data.data(), out.data.size()); } if (args.test_mode == TestMode::CODEGEN) return 0; auto c_definitions = pmresult->get(); auto& bytecode = pmresult->get(); return run_bpftrace(bpftrace, args.output_file, args.output_format, c_definitions, bytecode, std::move(args.named_params)); } bpftrace-0.24.1/src/map_info.h000066400000000000000000000042111506776124200161240ustar00rootroot00000000000000#pragma once #include #include #include "types.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace { struct HistogramArgs { long bits = -1; bool scalar = true; bool operator==(const HistogramArgs &other) { return bits == other.bits && scalar == other.scalar; } bool operator!=(const HistogramArgs &other) { return !(*this == other); } private: friend class cereal::access; template void serialize(Archive &archive) { archive(bits, scalar); } }; struct LinearHistogramArgs { long min = -1; long max = -1; long step = -1; bool scalar = true; bool operator==(const LinearHistogramArgs &other) { return min == other.min && max == other.max && step == other.step && scalar == other.scalar; } bool operator!=(const LinearHistogramArgs &other) { return !(*this == other); } private: friend class cereal::access; template void serialize(Archive &archive) { archive(min, max, step, scalar); } }; struct TSeriesArgs { long interval_ns = -1; long num_intervals = -1; SizedType value_type; TSeriesAggFunc agg; bool operator==(const TSeriesArgs &other) { return interval_ns == other.interval_ns && num_intervals == other.num_intervals && value_type == other.value_type && agg == other.agg; } bool operator!=(const TSeriesArgs &other) { return !(*this == other); } private: friend class cereal::access; template void serialize(Archive &archive) { archive(interval_ns, num_intervals, value_type, agg); } }; struct MapInfo { SizedType key_type; SizedType value_type; std::variant detail; int id = -1; int max_entries = -1; libbpf::bpf_map_type bpf_type = libbpf::BPF_MAP_TYPE_HASH; bool is_scalar = false; private: friend class cereal::access; template void serialize(Archive &archive) { archive(key_type, value_type, detail, id, max_entries, bpf_type, is_scalar); } }; } // namespace bpftrace bpftrace-0.24.1/src/output/000077500000000000000000000000001506776124200155255ustar00rootroot00000000000000bpftrace-0.24.1/src/output/CMakeLists.txt000066400000000000000000000001441506776124200202640ustar00rootroot00000000000000add_library(output STATIC json.cpp output.cpp text.cpp ) target_link_libraries(output util) bpftrace-0.24.1/src/output/json.cpp000066400000000000000000000321121506776124200172010ustar00rootroot00000000000000#include #include #include "output/json.h" namespace bpftrace::output { template struct JsonEmitter; template <> struct JsonEmitter { static void emit(std::ostream &out, const bool &v) { if (v) { out << "true"; } else { out << "false"; } } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const std::string &s) { out << "\""; for (const char c : s) { switch (c) { case '"': out << "\\\""; break; case '\\': out << "\\\\"; break; case '\n': out << "\\n"; break; case '\r': out << "\\r"; break; case '\t': out << "\\t"; break; default: // c always >= '\x00' if (c <= '\x1f') { out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(c); } else { out << c; } } } out << "\""; } }; template requires(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) struct JsonEmitter { static void emit(std::ostream &out, const T &v) { // JSON does not support numbers other than floating point, therefore // we emit int64_t as a string if it is not representable as a 64-bit // floating point. This is encoded in the spec in RFC 8259, and is a // common footgun people encounter at some point after choosing JSON. auto s = std::to_string(v); if constexpr (!std::is_same_v) { if (static_cast(static_cast(v)) != v) { return JsonEmitter::emit(out, s); } } out << s; } }; template struct JsonEmitter>> { static void emit(std::ostream &out, const std::vector> &m) { out << "{"; bool first = true; for (const auto &[key, value] : m) { if (!first) { out << ", "; // N.B. Objects are spaced, see below. } // Keys are always converted to strings. If this corresponds to a tuple // string (e.g. "(1, 2)"), then we explicitly strip off the parentheses. std::string s; if constexpr (std::is_same_v, std::string>) { s = key; } else { std::stringstream ss; ss << Primitive(key); s = ss.str(); if (s.size() >= 2 && s[0] == '(' && s[s.size() - 1] == ')') { s = s.substr(1, s.size() - 2); } // We also don't like spaces in the key, so we strip those too. Note // that this is different from the text representation, which does // still include spaces for most tuples. std::erase(s, ' '); } JsonEmitter::emit(out, s); // Leave the values as they are. out << ": "; JsonEmitter>::emit(out, value); first = false; } out << "}"; } }; template struct JsonEmitter> { static void emit(std::ostream &out, const std::vector &v) { out << "["; bool first = true; for (const auto &elem : v) { if (!first) { out << ","; // N.B. Arrays are unspaced, see above. } JsonEmitter::emit(out, elem); first = false; } out << "]"; } }; template struct JsonEmitter> { static void emit(std::ostream &out, const std::variant &v) { std::visit( [&](const auto &v) { JsonEmitter>::emit(out, v); }, v); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, [[maybe_unused]] const std::monostate &v) { out << "null"; } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive &v) { JsonEmitter::emit(out, v.variant); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive::Record &v) { JsonEmitter>::emit(out, v.fields); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive::Array &v) { JsonEmitter>::emit(out, v.values); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive::Buffer &v) { JsonEmitter>::emit(out, v.data); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive::Tuple &v) { JsonEmitter>::emit(out, v.values); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive::Symbolic &v) { // JSON does not emit symbolic values. JsonEmitter::emit(out, v.numeric); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive::Timestamp &v) { std::stringstream ss; ss << v; // Use default representation. JsonEmitter::emit(out, ss.str()); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Primitive::Duration &v) { std::stringstream ss; ss << v; // Use default representation. JsonEmitter::emit(out, ss.str()); } }; template std::optional one_less(const T &v) { if constexpr (std::is_same_v) { return one_less(v.numeric); } else if constexpr (std::is_same_v) { return v - 1; } else if constexpr (std::is_same_v) { if (v == 0) { return -1; } else { // N.B. This can overflow, but what can we do? We can't even encode this // integer into JSON properly, so this isn't the biggest problem. return static_cast(v - 1); } } else if constexpr (std::is_same_v) { // Try to recursively unpack the value and return one less. return std::visit([](const auto &v) { return one_less(v); }, v.variant); } else { // Give up. return std::nullopt; } } template <> struct JsonEmitter { static void emit(std::ostream &out, const Value::Histogram &hist) { out << "["; bool first = true; for (size_t i = 0; i < hist.counts.size(); i++) { if (!first) { out << ","; } out << "{"; if (i == 0 && hist.lower_bound) { out << "\"min\": "; JsonEmitter::emit(out, *hist.lower_bound); out << ", "; } else if (i > 0) { out << "\"min\": "; JsonEmitter::emit(out, hist.labels[i - 1]); out << ", "; } if (i < hist.labels.size()) { // For whatever reason, the open-intervals for the JSON encoding are // not open-intervals, they are closed intervals. So we need to // subtract one from the integer extracted here. // // If we can't find a suitable "one less" representation, then we // just emit the label as is (be it string, whatever). out << "\"max\": "; auto v = one_less(hist.labels[i]); if (v) { JsonEmitter::emit(out, *v); } else { JsonEmitter::emit(out, hist.labels[i]); } out << ", "; } out << "\"count\": " << hist.counts[i]; out << "}"; first = false; } out << "]"; } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Value::OrderedMap &m) { JsonEmitter>::emit(out, m.values); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Value::Stats &s) { JsonEmitter>::emit(out, s.value); } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Value::TimeSeries &tseries) { bool first = true; out << "["; for (const auto &[ts, value] : tseries.values) { if (!first) { out << ","; } out << "{\"interval_start\":" << ts << ",\"value\":" << value << "}"; } out << "]"; } }; template <> struct JsonEmitter { static void emit(std::ostream &out, const Value &v) { JsonEmitter::emit(out, v.variant); } }; template void emit_data(std::ostream &out, const std::string &type, std::optional &&name, const T &v) { out << R"({"type": ")" << type << R"(", "data": )"; if (name) { out << "{"; JsonEmitter::emit(out, *name); out << ": "; } JsonEmitter::emit(out, v); if (name) { out << "}"; } out << "}" << std::endl; } template bool has_type(const Value &value) { if (std::holds_alternative(value.variant)) { return true; } if (std::holds_alternative>(value.variant)) { const auto &vec = std::get>(value.variant); if (!vec.empty()) { return has_type(vec.at(0)); } } if (std::holds_alternative(value.variant)) { const auto &m = std::get(value.variant); if (!m.values.empty()) { return has_type(m.values.at(0).second); } } return false; } void JsonOutput::map(const std::string &name, const Value &value) { // If the value is a histogram, or a map of histograms, then we set the type // to `hist`. If it is explicitly a `stats` map, then set that type. // Otherwise, just set the message type to `map`. std::string type = "map"; if (has_type(value)) { type = "stats"; } else if (has_type(value)) { type = "tseries"; } else if (has_type(value)) { type = "hist"; } emit_data(out_, type, name, value); } void JsonOutput::value(const Value &value) { emit_data(out_, "value", std::nullopt, value); } void JsonOutput::printf(const std::string &str) { emit_data(out_, "printf", std::nullopt, str); } void JsonOutput::errorf(const std::string &str, const SourceInfo &info) { out_ << R"({"type": "errorf")"; out_ << R"(, "msg": )"; std::stringstream ss; ss << str; JsonEmitter::emit(out_, ss.str()); // Json only prints the top level location out_ << R"(, "filename": )"; JsonEmitter::emit(out_, info.locations.begin()->filename); out_ << R"(, "line": )"; JsonEmitter::emit(out_, info.locations.begin()->line); out_ << R"(, "col": )"; JsonEmitter::emit(out_, info.locations.begin()->column); out_ << R"(})" << std::endl; } void JsonOutput::time(const std::string &time) { emit_data(out_, "time", std::nullopt, time); } void JsonOutput::cat(const std::string &cat) { emit_data(out_, "cat", std::nullopt, cat); } void JsonOutput::join(const std::string &join) { emit_data(out_, "join", std::nullopt, join); } void JsonOutput::syscall(const std::string &syscall) { emit_data(out_, "syscall", std::nullopt, syscall); } void JsonOutput::lost_events(uint64_t lost) { // This is a special case, it emits both a count and the `data` field. out_ << R"({"type": "lost_events", "count": )" << lost << R"(, "data": {"events": )" << lost << "}}" << std::endl; } void JsonOutput::attached_probes(uint64_t num_probes) { // As with lost_events, this is a special case, we do a `count` and `data` // field. out_ << R"({"type": "attached_probes", "count": )" << num_probes << R"(, "data": {"probes": )" << num_probes << "}}" << std::endl; } void JsonOutput::runtime_error(int retcode, const RuntimeErrorInfo &info) { switch (info.error_id) { case RuntimeErrorId::HELPER_ERROR: { out_ << R"({"type": "helper_error")"; out_ << R"(, "msg": )"; JsonEmitter::emit(out_, strerror(-retcode)); out_ << R"(, "helper": )"; std::stringstream ss; ss << info.func_id; JsonEmitter::emit(out_, ss.str()); out_ << R"(, "retcode": )"; JsonEmitter::emit(out_, retcode); break; } default: { out_ << R"({"type": "runtime_error")"; out_ << R"(, "msg": )"; std::stringstream ss; ss << info; JsonEmitter::emit(out_, ss.str()); break; } } // Json only prints the top level location out_ << R"(, "filename": )"; JsonEmitter::emit(out_, info.locations.begin()->filename); out_ << R"(, "line": )"; JsonEmitter::emit(out_, info.locations.begin()->line); out_ << R"(, "col": )"; JsonEmitter::emit(out_, info.locations.begin()->column); out_ << R"(})" << std::endl; } void JsonOutput::benchmark_results( const std::vector> &results) { emit_data(out_, "benchmark_results", std::nullopt, results); } void JsonOutput::end() { // Nothing emitted. } } // namespace bpftrace::output bpftrace-0.24.1/src/output/json.h000066400000000000000000000017421506776124200166530ustar00rootroot00000000000000#pragma once #include #include "output/output.h" namespace bpftrace::output { class JsonOutput : public Output { public: explicit JsonOutput(std::ostream &out = std::cout) : out_(out) {}; void map(const std::string &name, const Value &value) override; void value(const Value &value) override; void printf(const std::string &str) override; void errorf(const std::string &str, const SourceInfo &info) override; void time(const std::string &time) override; void cat(const std::string &cat) override; void join(const std::string &join) override; void syscall(const std::string &syscall) override; void lost_events(uint64_t lost) override; void attached_probes(uint64_t num_probes) override; void runtime_error(int retcode, const RuntimeErrorInfo &info) override; void benchmark_results( const std::vector> &results) override; void end() override; private: std::ostream &out_; }; } // namespace bpftrace::output bpftrace-0.24.1/src/output/output.cpp000066400000000000000000000016001506776124200175660ustar00rootroot00000000000000#include "output/output.h" #include "output/text.h" namespace bpftrace::output { std::partial_ordering Primitive::Array::operator<=>(const Array &other) const { return values <=> other.values; } bool Primitive::Array::operator==(const Array &other) const { return values == other.values; } std::partial_ordering Primitive::Tuple::operator<=>(const Tuple &other) const { return values <=> other.values; } bool Primitive::Tuple::operator==(const Tuple &other) const { return values == other.values; } std::partial_ordering Primitive::Record::operator<=>(const Record &other) const { return fields <=> other.fields; } bool Primitive::Record::operator==(const Record &other) const { return fields == other.fields; } std::ostream &operator<<(std::ostream &out, const Primitive &p) { TextOutput output(out); output.primitive(p); return out; } } // namespace bpftrace::output bpftrace-0.24.1/src/output/output.h000066400000000000000000000131621506776124200172410ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "required_resources.h" // For RuntimeErrorInfo. namespace bpftrace::output { // Primitive is a basic value. // // This covers basic atoms, arrays, structures, etc. Primitives are guaranteed // to be orderable and comparable. // // It does not cover any advanced maps. struct Primitive { struct Array { std::vector values; std::partial_ordering operator<=>(const Array& other) const; bool operator==(const Array& other) const; }; struct Buffer { std::vector data; std::strong_ordering operator<=>(const Buffer& other) const = default; bool operator==(const Buffer& other) const = default; }; struct Tuple { std::vector values; std::partial_ordering operator<=>(const Tuple& other) const; bool operator==(const Tuple& other) const; }; struct Record { std::vector> fields; std::partial_ordering operator<=>(const Record& other) const; bool operator==(const Record& other) const; }; struct Symbolic { Symbolic(std::string s, uint64_t n) : symbol(std::move(s)), numeric(n) {}; std::string symbol; uint64_t numeric; auto operator<=>(const Symbolic& other) const = default; bool operator==(const Symbolic& other) const = default; }; using Timestamp = std::chrono::time_point; using Duration = std::chrono::duration; using Variant = std::variant; template Primitive(T&& t) requires std::constructible_from : variant(std::forward(t)){}; auto operator<=>(const Primitive& other) const = default; bool operator==(const Primitive& other) const = default; Variant variant; }; // Primitives have a default representation. std::ostream& operator<<(std::ostream& out, const Primitive& p); // Value is an arbitrary value. // // This is a primitive, or a high-level aggregation over values. It is not // guaranteed to be orderable or comparable. struct Value { // Histogram is a specific type of value, which contains a sequence // of labels that represent the end interval for buckets, and a sequence of // counts. All the labels are typically numbers, strings or `Symbolic`. // // The labels field represents the upper bounds for each of the buckets, // and if counts is larger than labels then the top bucket has no upper bound. // // If there is a lower-bound for the first bucket, it is specified within the // lower_bound field. struct Histogram { std::optional lower_bound; std::vector labels; std::vector counts; }; // OrderedMap defines a map that can be ordered externally, and therefore // is not necessarily bound to be ordered by key. struct OrderedMap { std::vector> values; }; // Stats is just an arbitrary classifier for another value. It changes // only some of the encoding used by some outputs. struct Stats { Stats(OrderedMap&& m) : value(std::move(m)) {}; Stats(Primitive&& p) : value(std::move(p)) {}; std::variant value; }; // TimeSeries is a sequence of values that are collected over time. struct TimeSeries { std::vector> values; }; using Variant = std::variant, OrderedMap, Stats, TimeSeries>; template Value(T&& t) requires std::constructible_from : variant(std::forward(t)){}; Variant variant; }; // Abstract class for output. // // This should be overriden by individual implementations. class Output { public: virtual ~Output() = default; // Print high-level maps, with special support for histogram-type maps, // and "stats" maps, where richer statistics are required. The exact // statistics or method for publishing the histograms is up to the engine. // // The "stats" boolean indicates whether this should be considered as "stats" // map or not, in order to preserve message types for JSON encoding. virtual void map(const std::string& name, const Value& value) = 0; // Print an arbitrary value. virtual void value(const Value& value) = 0; // Specialized messages during execution. virtual void printf(const std::string& str) = 0; virtual void errorf(const std::string& str, const SourceInfo& info) = 0; virtual void time(const std::string& time) = 0; virtual void cat(const std::string& cat) = 0; virtual void join(const std::string& join) = 0; virtual void syscall(const std::string& syscall) = 0; virtual void lost_events(uint64_t lost) = 0; virtual void attached_probes(uint64_t num_probes) = 0; virtual void runtime_error(int retcode, const RuntimeErrorInfo& info) = 0; virtual void benchmark_results( const std::vector>& results) = 0; virtual void end() = 0; }; } // namespace bpftrace::output bpftrace-0.24.1/src/output/text.cpp000066400000000000000000000471341506776124200172260ustar00rootroot00000000000000#include #include #include "log.h" #include "output/text.h" #include "util/strings.h" #include "util/time.h" namespace bpftrace::output { using namespace std::chrono_literals; template struct TextEmitter { static void emit(std::ostream &out, const T &v) { out << v; } }; template <> struct TextEmitter { static void emit(std::ostream &out, const bool &v) { if (v) { out << "true"; } else { out << "false"; } } }; template struct TextEmitter> { static void emit(std::ostream &out, const std::variant &v) { std::visit( [&](const auto &v) { TextEmitter>::emit(out, v); }, v); } }; template struct TextEmitter> { static void emit(std::ostream &out, const std::vector &v) { bool first = true; out << "["; for (const auto &elem : v) { if (!first) { out << ","; } TextEmitter::emit(out, elem); first = false; } out << "]"; } }; template <> struct TextEmitter { static void emit(std::ostream &out, [[maybe_unused]] const std::monostate &v) { out << "null"; } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive &v) { TextEmitter::emit(out, v.variant); } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive::Record &v) { bool first = true; if (v.fields.empty()) { out << "{}"; return; } out << "{ "; for (const auto &[key, elem] : v.fields) { if (!first) { out << ", "; // Structs always get spaces. } out << "."; TextEmitter::emit(out, key); out << " = "; TextEmitter::emit(out, elem); first = false; } out << " }"; } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive::Array &v) { TextEmitter>::emit(out, v.values); } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive::Buffer &v) { TextEmitter::emit( out, util::hex_format_buffer(v.data.data(), v.data.size())); } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive::Tuple &v) { bool first = true; out << "("; for (const auto &elem : v.values) { if (!first) { out << ", "; // N.B. tuples get spaces, arrays do not. } TextEmitter::emit(out, elem); first = false; } out << ")"; } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive::Symbolic &v) { // Always emit the symbolic value. TextEmitter::emit(out, v.symbol); } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive::Timestamp &v, const std::string &format = "%Y-%m-%dT%H:%M:%S", const util::DisplayUnit &unit = util::DisplayUnit::ns) { // Emit the time point in the ISO 8601 form. auto s = std::chrono::time_point_cast(v); auto ns = std::chrono::duration_cast(v - s).count(); std::time_t t = std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(s.time_since_epoch())); std::tm tm; localtime_r(&t, &tm); std::stringstream ss; ss << std::put_time(&tm, format.c_str()); switch (unit) { case util::DisplayUnit::ns: ss << "." << std::setw(9) << std::setfill('0') << ns; break; case util::DisplayUnit::us: ss << "." << std::setw(6) << std::setfill('0') << ns / 1000; break; case util::DisplayUnit::ms: ss << "." << std::setw(3) << std::setfill('0') << ns / 1000000; break; default: // No additional trailing seconds. break; } TextEmitter::emit(out, ss.str()); } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Primitive::Duration &v) { // Just emit a string with the duration in human-readable format. auto [unit, scale] = util::duration_str(v); out << (v.count() / scale) << unit; } }; template static bool adjacent_values(const std::variant &first, const std::variant &second) { if (!std::holds_alternative(first) || !std::holds_alternative(second)) { return false; } return (std::get(first) + static_cast(1)) == std::get(second); } static bool single_value(const Primitive &first, const Primitive &second) { return adjacent_values(first.variant, second.variant) || adjacent_values(first.variant, second.variant); } template <> struct TextEmitter { static void emit(std::ostream &out, const Value::Histogram &hist) { uint64_t max_value = 0; for (const auto &v : hist.counts) { max_value = std::max(max_value, v); } for (size_t i = 0; i < hist.counts.size() || i < hist.labels.size(); i++) { int max_width = 52; int bar_width = (hist.counts.at(i) / static_cast(max_value)) * max_width; std::ostringstream header; if (i == 0) { if (!hist.lower_bound) { header << "(..., "; TextEmitter::emit(header, hist.labels[i]); header << ")"; } else if (single_value(*hist.lower_bound, hist.labels[i])) { header << "["; TextEmitter::emit(header, *hist.lower_bound); header << "]"; } else { header << "["; TextEmitter::emit(header, *hist.lower_bound); header << ", "; TextEmitter::emit(header, hist.labels[i]); header << ")"; } } else if (i >= hist.labels.size()) { header << "["; TextEmitter::emit(header, hist.labels[i - 1]); header << ", ...)"; } else if (single_value(hist.labels[i - 1], hist.labels[i])) { header << "["; TextEmitter::emit(header, hist.labels[i - 1]); header << "]"; } else { header << "["; TextEmitter::emit(header, hist.labels[i - 1]); header << ", "; TextEmitter::emit(header, hist.labels[i]); header << ")"; } std::string bar(bar_width, '@'); out << std::setw(16) << std::left << header.str() << std::setw(8) << std::right << hist.counts.at(i) << " |" << std::setw(max_width) << std::left << bar << "|" << std::endl; } } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Value &v) { TextEmitter::emit(out, v.variant); } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Value::OrderedMap &m) { for (const auto &[key, value] : m.values) { out << "["; TextEmitter::emit(out, key); out << "]: "; TextEmitter::emit(out, value); out << std::endl; } } }; template <> struct TextEmitter { static void emit(std::ostream &out, const Value::Stats &s) { TextEmitter>::emit(out, s.value); } }; static void try_dec(Primitive &p) { if (std::holds_alternative(p.variant) && std::get(p.variant) > std::numeric_limits::min()) { std::get(p.variant)--; } else if (std::holds_alternative(p.variant) && std::get(p.variant) > std::numeric_limits::min()) { std::get(p.variant)--; } } static void try_inc(Primitive &p) { if (std::holds_alternative(p.variant) && std::get(p.variant) < std::numeric_limits::max()) { std::get(p.variant)++; } else if (std::holds_alternative(p.variant) && std::get(p.variant) < std::numeric_limits::max()) { std::get(p.variant)++; } } static int64_t distance(const Primitive &p, int64_t other) { if (std::holds_alternative(p.variant)) { const auto &v = std::get(p.variant); if (other < 0) { return static_cast(-other) + v; } if (v > static_cast(other)) { return static_cast(v - static_cast(other)); } else { return -static_cast(static_cast(other) - v); } } if (std::holds_alternative(p.variant)) { const auto &v = std::get(p.variant); return v - other; } return 0; } static int64_t distance(const Primitive &p, const Primitive &other) { if (std::holds_alternative(other.variant)) { return distance(p, std::get(other.variant)); } else if (std::holds_alternative(other.variant)) { return distance(p, static_cast(std::get(other.variant))); } else { return 0; // Only handle integer primitives for now. } } template <> struct TextEmitter { static void emit(std::ostream &out, const Value::TimeSeries &ts) { constexpr int graph_width = 53; if (ts.values.empty()) { out << ""; return; } std::vector times; output::Primitive min_value = ts.values.front().second; output::Primitive max_value = ts.values.front().second; for (const auto &[epoch, v] : ts.values) { if (std::holds_alternative(v.variant)) { continue; // Skip missing values. } if (v < min_value) { min_value = v; } if (v > max_value) { max_value = v; } } if (min_value == max_value) { // Generally prefer if we can add some buffer on both sides of the // points in the graph, but don't overflow if our min or max is at the // upper bound of the range. try_dec(min_value); try_inc(max_value); } // Figure out the interval. const auto interval = (ts.values.back().first - ts.values.front().first) / (ts.values.size() > 1 ? ts.values.size() - 1 : 1); const auto [unit, scale] = util::duration_str(interval); std::string time_fmt = "hh:mm:ss"; size_t time_size = time_fmt.size(); if (unit > util::DisplayUnit::s) { std::stringstream ss; ss << "." << unit; time_fmt += ss.str(); time_size += 1 + static_cast(unit); // See DisplayUnit. } static const char *time_format = "%H:%M:%S"; constexpr int padding = 21; std::stringstream res; res << std::setw(time_size + 1) << ""; res << std::setw(padding); res << std::left; TextEmitter::emit(res, min_value); res << std::setw(graph_width - padding); res << std::right; TextEmitter::emit(res, max_value); res << std::endl; std::string top(graph_width - 2, '_'); res << std::setw(time_size + 1) << std::left << time_fmt; res << "|" << top << "|" << std::endl; int zero_offset = 0; if (distance(min_value, 0) < 0 && distance(0, max_value) < 0) { zero_offset = static_cast(graph_width) * static_cast(distance(min_value, 0)) / static_cast(distance(min_value, max_value)); } for (const auto &[epoch, v] : ts.values) { std::string line(graph_width, ' '); TextEmitter::emit(res, epoch, time_format, unit); res << " "; line[0] = '|'; line[graph_width - 1] = '|'; if (zero_offset > 0) { line[zero_offset] = '.'; } // There may be epochs with no valid entry. if (!std::holds_alternative(v.variant)) { int point_offset = graph_width / 2; if (min_value != max_value) { point_offset = static_cast(graph_width - 1) * static_cast(distance(min_value, v)) / static_cast(distance(min_value, max_value)); } // Ensure that we don't have floating point issues. line[std::max(0, std::min(graph_width - 1, point_offset))] = '*'; res << line << " "; TextEmitter::emit(res, v); } else { res << line << " -"; } res << std::endl; } std::string bottom(graph_width, '_'); bottom[0] = 'v'; bottom[graph_width - 1] = 'v'; res << std::setw(time_size) << "" << " " << bottom << std::endl; res << std::setw(time_size) << "" << " "; res << std::setw(padding); res << std::left; TextEmitter::emit(res, min_value); res << std::setw(graph_width - padding); res << std::right; TextEmitter::emit(res, max_value); res << std::endl; out << res.str(); } }; static void emit_map(std::ostream &out, const std::string &name, const Value::OrderedMap &m) { // For legacy reasons, this is printed with the map name each time. Also, // we have special behavior for the case of a tuple-based keys, wherein // the tuple parenthesis are explicitly omitted for this line only. for (const auto &[key, value] : m.values) { out << name << "["; std::stringstream ss; ss << key; auto s = ss.str(); if (s.size() >= 2 && s[0] == '(' && s[s.size() - 1] == ')') { s = s.substr(1, s.size() - 2); } out << s; // If this is a primitive (and fits on a single line), then we emit // inline, otherwise we display the potentially multi-line value on // subsequent lines. if (!std::holds_alternative(value.variant)) { out << "]:"; out << std::endl; } else { out << "]: "; } TextEmitter::emit(out, value); out << std::endl; } } void TextOutput::map(const std::string &name, const Value &value) { if (std::holds_alternative(value.variant) || std::holds_alternative>(value.variant) || std::holds_alternative(value.variant)) { // These are printed on separate lines. out_ << name << ":" << std::endl; TextEmitter::emit(out_, value); out_ << std::endl; } else if (std::holds_alternative(value.variant)) { // Treat this as the top-level value and unpack. auto stats = std::get(value.variant); if (std::holds_alternative(stats.value)) { // Print as a normal map, per below. emit_map(out_, name, std::get(stats.value)); } else { // Print as a normal value, per below. out_ << name << ": "; TextEmitter::emit(out_, std::get(stats.value)); out_ << std::endl; } } else if (std::holds_alternative(value.variant)) { emit_map(out_, name, std::get(value.variant)); } else { // Single value. out_ << name << ": "; TextEmitter::emit(out_, value); out_ << std::endl; } } void TextOutput::value(const Value &value) { TextEmitter::emit(out_, value); out_ << std::endl; } void TextOutput::primitive(const Primitive &p) { TextEmitter::emit(out_, p); } void TextOutput::printf(const std::string &str) { out_ << str; } void TextOutput::errorf(const std::string &str, const SourceInfo &info) { bool first = true; for (const auto &loc : info.locations) { if (first) { // No need to print the source context as that's just the `errorf` // call LOG(ERROR, std::string(loc.source_location), err_) << str; first = false; } else { LOG(ERROR, std::string(loc.source_location), std::vector(loc.source_context), err_) << "expanded from"; } } } void TextOutput::time(const std::string &time) { out_ << time; } void TextOutput::cat(const std::string &cat) { out_ << cat; } void TextOutput::join(const std::string &join) { out_ << join << std::endl; } void TextOutput::syscall(const std::string &syscall) { out_ << syscall << std::endl; } void TextOutput::lost_events(uint64_t lost) { err_ << "Lost " << lost << " events" << std::endl; } void TextOutput::attached_probes(uint64_t num_probes) { if (num_probes == 1) err_ << "Attached " << num_probes << " probe" << std::endl; else err_ << "Attached " << num_probes << " probes" << std::endl; } void TextOutput::runtime_error(int retcode, const RuntimeErrorInfo &info) { switch (info.error_id) { case RuntimeErrorId::HELPER_ERROR: { std::string msg; if (info.func_id == libbpf::BPF_FUNC_map_update_elem && retcode == -E2BIG) { msg = "Map full; can't update element. Try increasing max_map_keys " "config " "or manually setting the max entries in a map declaration e.g. " "`let " "@a = hash(5000)`"; } else if (info.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 (info.func_id == libbpf::BPF_FUNC_map_lookup_elem) { msg = "Can't lookup map element because it does not exist."; } else { msg = strerror(-retcode); } LOG(WARNING, std::string(info.locations.begin()->source_location), std::vector(info.locations.begin()->source_context), err_) << msg << "\nAdditional Info - helper: " << info.func_id << ", retcode: " << retcode; break; } default: { LOG(WARNING, std::string(info.locations.begin()->source_location), std::vector(info.locations.begin()->source_context), err_) << info; break; } } // Print the chain for (size_t i = 1; i < info.locations.size(); ++i) { LOG(WARNING, std::string(info.locations[i].source_location), std::vector(info.locations[i].source_context), err_) << "expanded from"; } } void TextOutput::benchmark_results( const std::vector> &results) { const std::string BENCHMARK = "BENCHMARK"; const std::string AVERAGE_TIME = "AVERAGE TIME"; size_t longest_name = BENCHMARK.size(); for (const auto &benchmark : results) { longest_name = std::max(longest_name, benchmark.first.size()); } auto sep = [&]() { out_ << "+" << std::setw(longest_name + 2) << std::setfill('-') << "" << "+" << std::setw(AVERAGE_TIME.size() + 2) << std::setfill('-') << "" << "+" << std::endl; }; sep(); out_ << "| " << std::left << std::setw(longest_name) << std::setfill(' ') << BENCHMARK << " | " << AVERAGE_TIME << " |" << std::endl; sep(); for (const auto &benchmark : results) { auto [unit, scale] = util::duration_str( std::chrono::nanoseconds(benchmark.second)); std::stringstream val; val << benchmark.second / scale << unit; out_ << "| " << std::left << std::setw(longest_name) << std::setfill(' ') << benchmark.first << " | " << std::left << std::setw(AVERAGE_TIME.size()) << std::setfill(' ') << val.str() << " |" << std::endl; } sep(); out_ << std::endl; } void TextOutput::end() { out_ << std::endl; out_ << std::endl; } } // namespace bpftrace::output bpftrace-0.24.1/src/output/text.h000066400000000000000000000022251506776124200166630ustar00rootroot00000000000000#pragma once #include #include "output/output.h" namespace bpftrace::output { class TextOutput : public Output { public: explicit TextOutput(std::ostream &out = std::cout, std::ostream &err = std::cerr) : out_(out), err_(err) {}; void map(const std::string &name, const Value &value) override; void value(const Value &value) override; void printf(const std::string &str) override; void errorf(const std::string &str, const SourceInfo &info) override; void time(const std::string &time) override; void cat(const std::string &cat) override; void join(const std::string &join) override; void syscall(const std::string &syscall) override; void lost_events(uint64_t lost) override; void attached_probes(uint64_t num_probes) override; void runtime_error(int retcode, const RuntimeErrorInfo &info) override; void benchmark_results( const std::vector> &results) override; void end() override; // Allows formatting of a specific primitive. void primitive(const Primitive &p); private: std::ostream &out_; std::ostream &err_; }; } // namespace bpftrace::output bpftrace-0.24.1/src/parser.yy000066400000000000000000001023131506776124200160440ustar00rootroot00000000000000%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 0 %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 "ast/context.h" } %{ #include #include "driver.h" #include "parser.tab.hh" YY_DECL; 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" // Pseudo token; see below. LOW "low-precedence" ; %token BUILTIN "builtin" %token INT_TYPE "integer type" %token BUILTIN_TYPE "builtin type" %token SUBPROG "subprog" %token MACRO "macro" %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 UNSIGNED_INT "integer" %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" %token IMPORT "import" %token BOOL "bool" %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 bare_block %type block_expr %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 or_expr postfix_expr relational_expr shift_expr tuple_access_expr unary_expr xor_expr %type vargs %type subprog_arg %type subprog_args %type macro_args %type map %type map_expr %type param %type param_count %type pred %type config %type import_stmt %type imports %type assign_stmt block_stmt expr_stmt if_stmt jump_stmt loop_stmt for_stmt %type root_stmt macro map_decl_stmt subprog probe %type root_stmts %type range %type var_decl_stmt %type block block_or_if stmt_list %type config_assign_stmt %type config_assign_stmt_list config_block %type type int_type pointer_type struct_type %type var %type var_addr %type map_addr %type program // A pseudo token, which is the lowest precedence among all tokens. // // This helps us explicitly lower the precedence of a given rule to force shift // vs. reduce, and make the grammar explicit (still ambiguous, but explicitly // ambiguous). For example, consider the inherently ambiguous `@foo[..]`, which // could be interpreted as accessing the `@foo` non-scalar map, or indexing // into the value of the `@foo` scalar map, e.g. `(@foo)[...]`. We lower the // precedence of the associated rules to ensure that this is shifted, and the // longer `map_expr` rule will match over the `map` rule in this case. %left LOW %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 DOT PTR %right PAREN RPAREN %right LBRACKET RBRACKET // In order to support the parsing of full programs and the parsing of just // expressions (used while expanding C macros, for example), use the trick // described in Bison's FAQ [1]. // [1] https://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html %token START_PROGRAM "program" %token START_EXPR "expression" %start start %% start: START_PROGRAM program END { driver.result = $2; } | START_EXPR expr END { driver.result = $2; } ; program: c_definitions config imports root_stmts { $$ = driver.ctx.make_node($1, $2, std::move($3), std::move($4), @$); } ; 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(); } ; imports: imports import_stmt { $$ = std::move($1); $$.push_back($2); } | %empty { $$ = ast::ImportList{}; } ; import_stmt: IMPORT STRING ";" { $$ = driver.ctx.make_node($2, @$); } ; 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()}, {"avg_t", CreateAvg(true)}, {"stats_t", CreateStats(true)}, {"umin_t", CreateMin(false)}, {"umax_t", CreateMax(false)}, {"usum_t", CreateSum(false)}, {"uavg_t", CreateAvg(false)}, {"ustats_t", CreateStats(false)}, {"timestamp", CreateTimestamp()}, {"macaddr_t", CreateMacAddress()}, {"cgroup_path_t", CreateCgroupPath()}, {"strerror_t", CreateStrerror()}, {"string", CreateString(0)}, }; $$ = type_map[$1]; } | SIZED_TYPE { if ($1 == "inet") { $$ = CreateInet(0); } else if ($1 == "buffer") { $$ = CreateBuffer(0); } } | SIZED_TYPE "[" UNSIGNED_INT "]" { if ($1 == "inet") { $$ = CreateInet($3); } else if ($1 == "buffer") { $$ = CreateBuffer($3); } } | int_type "[" UNSIGNED_INT "]" { $$ = CreateArray($3, $1); } | struct_type "[" UNSIGNED_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::ConfigStatementList{}; } ; config_assign_stmt: IDENT ASSIGN UNSIGNED_INT { $$ = driver.ctx.make_node($1, $3, @$); } | IDENT ASSIGN IDENT { $$ = driver.ctx.make_node($1, $3, @$); } | IDENT ASSIGN STRING { $$ = driver.ctx.make_node($1, $3, @$); } | IDENT ASSIGN BOOL { $$ = driver.ctx.make_node($1, $3, @$); } ; 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, @$); } ; macro: MACRO IDENT "(" macro_args ")" block_expr { $$ = driver.ctx.make_node($2, std::move($4), $6, @$); } | MACRO IDENT "(" macro_args ")" bare_block { $$ = driver.ctx.make_node($2, std::move($4), $6, @$); } macro_args: macro_args "," map { $$ = std::move($1); $$.push_back($3); } | macro_args "," var { $$ = std::move($1); $$.push_back($3); } | macro_args "," ident { $$ = std::move($1); $$.push_back(driver.ctx.make_node($3, @$)); } | map { $$ = ast::ExpressionList{$1}; } | var { $$ = ast::ExpressionList{$1}; } | ident { $$ = ast::ExpressionList{driver.ctx.make_node($1, @$)}; } | %empty { $$ = ast::ExpressionList{}; } ; root_stmts: root_stmts root_stmt { $$ = std::move($1); $$.push_back($2); } | %empty { $$ = ast::RootStatements{}; } root_stmt: macro { $$ = $1; } | map_decl_stmt { $$ = $1; } | subprog { $$ = $1; } | probe { $$ = $1; } ; probe: attach_points pred block { $$ = driver.ctx.make_node(std::move($1), $2, driver.ctx.make_node(std::move($3), @3), @$); } ; 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. Note that this is a general escape hatch for many cases, // since we can't handle the general parsing and unparsing of e.g. // integer types that use `_` separators, or exponential notation, // or hex vs. non-hex representation etc. | attach_point_def STRING { $$ = $1 + "\"" + std::regex_replace($2, std::regex("\""), "\\\"") + "\""; } | attach_point_def UNSIGNED_INT { $$ = $1 + std::to_string($2); } | attach_point_def PATH { $$ = $1 + $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 { // "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(n, @$); } catch (std::exception const& e) { error(@1, "param " + $1 + " is out of integer range [1, " + std::to_string(std::numeric_limits::max()) + "]"); YYERROR; } } ; param_count: PARAMCOUNT { $$ = driver.ctx.make_node(@$); } ; /* * 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; } | bare_block { $$ = $1; } ; bare_block: "{" stmt_list "}" { $$ = driver.ctx.make_node(std::move($2), @2); } 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 "(" expr ")" 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 ":" map ")" block { $$ = driver.ctx.make_node($3, $5, std::move($7), @1); } | FOR "(" var ":" range ")" block { $$ = driver.ctx.make_node($3, $5, std::move($7), @1); } ; range: postfix_expr DOT DOT postfix_expr { $$ = driver.ctx.make_node($1, $4, @$); } ; 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, @$); } | map_expr ASSIGN expr { $$ = driver.ctx.make_node($1->map, $1->key, $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, @$); } | map_expr compound_op expr { auto b = driver.ctx.make_node($1, $2, $3, @2); $$ = driver.ctx.make_node($1->map, $1->key, b, @$); } | var compound_op expr { auto b = driver.ctx.make_node($1, $2, $3, @2); $$ = driver.ctx.make_node($1, b, @$); } ; map_decl_stmt: LET MAP ASSIGN IDENT LPAREN UNSIGNED_INT RPAREN ";" { $$ = driver.ctx.make_node($2, $4, $6, @$); } ; var_decl_stmt: LET var { $$ = driver.ctx.make_node($2, @$); } | LET var COLON type { $$ = driver.ctx.make_node($2, $4, @$); } ; primary_expr: UNSIGNED_INT { $$ = driver.ctx.make_node($1, @$); } | BOOL { $$ = driver.ctx.make_node($1, @$); } | STRING { $$ = driver.ctx.make_node($1, @$); } | BUILTIN { $$ = driver.ctx.make_node($1, @$); } | LPAREN expr RPAREN { $$ = $2; } | param { $$ = $1; } | param_count { $$ = $1; } | var { $$ = $1; } | var_addr { $$ = $1; } | map_addr { $$ = $1; } | map_expr { $$ = $1; } | "(" vargs "," expr ")" { auto &args = $2; args.push_back($4); $$ = driver.ctx.make_node(std::move(args), @$); } | map %prec LOW { $$ = $1; } | IDENT %prec LOW { $$ = driver.ctx.make_node($1, @$); } ; 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($1, ast::Operator::MUL, 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; } | var INCREMENT { $$ = driver.ctx.make_node($1, ast::Operator::INCREMENT, true, @2); } | var DECREMENT { $$ = driver.ctx.make_node($1, ast::Operator::DECREMENT, true, @2); } | map INCREMENT { $$ = driver.ctx.make_node($1, ast::Operator::INCREMENT, true, @2); } | map DECREMENT { $$ = driver.ctx.make_node($1, ast::Operator::DECREMENT, true, @2); } | map_expr INCREMENT { $$ = driver.ctx.make_node($1, ast::Operator::INCREMENT, true, @2); } | map_expr DECREMENT { $$ = driver.ctx.make_node($1, ast::Operator::DECREMENT, 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 UNSIGNED_INT { $$ = driver.ctx.make_node($1, $3, @3); } ; block_expr: "{" stmt_list expr "}" { $$ = driver.ctx.make_node(std::move($2), $3, @$); } ; unary_expr: unary_op cast_expr { $$ = driver.ctx.make_node($2, $1, false, @1); } | postfix_expr { $$ = $1; } | INCREMENT var { $$ = driver.ctx.make_node($2, ast::Operator::INCREMENT, false, @1); } | DECREMENT var { $$ = driver.ctx.make_node($2, ast::Operator::DECREMENT, false, @1); } | INCREMENT map { $$ = driver.ctx.make_node($2, ast::Operator::INCREMENT, false, @1); } | DECREMENT map { $$ = driver.ctx.make_node($2, ast::Operator::DECREMENT, false, @1); } | INCREMENT map_expr { $$ = driver.ctx.make_node($2, ast::Operator::INCREMENT, false, @1); } | DECREMENT map_expr { $$ = driver.ctx.make_node($2, ast::Operator::DECREMENT, false, @1); } | block_expr { $$ = $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, @$); } ; 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; } | SIZED_TYPE { $$ = $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: IDENT "(" ")" { $$ = driver.ctx.make_node($1, @$); } | BUILTIN "(" ")" { $$ = driver.ctx.make_node($1, @$); } | IDENT "(" vargs ")" { $$ = driver.ctx.make_node($1, std::move($3), @$); } | BUILTIN "(" vargs ")" { $$ = driver.ctx.make_node($1, std::move($3), @$); } ; map: MAP { $$ = driver.ctx.make_node($1, @$); } map_expr: 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, @$); } ; var_addr: BAND var { $$ = driver.ctx.make_node($2, @$); } ; map_addr: BAND map { $$ = driver.ctx.make_node($2, @$); } ; 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.24.1/src/pcap_writer.cpp000066400000000000000000000025011506776124200172060ustar00rootroot00000000000000#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, const 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__)), const void *pkt __attribute__((__unused__)), unsigned int size __attribute__((__unused__))) { return false; } }; // namespace bpftrace #endif // HAVE_LIBPCAP bpftrace-0.24.1/src/pcap_writer.h000066400000000000000000000007041506776124200166560ustar00rootroot00000000000000#pragma once #include #include struct pcap; using pcap_t = struct pcap; struct pcap_dumper; using pcap_dumper_t = struct pcap_dumper; namespace bpftrace { class PCAPwriter { public: PCAPwriter() = default; ~PCAPwriter() = default; bool open(std::string file); void close(); bool write(uint64_t ns, const void *pkt, unsigned int size); private: pcap_t *pd_; pcap_dumper_t *pdumper_; }; }; // namespace bpftrace bpftrace-0.24.1/src/probe_matcher.cpp000066400000000000000000000506471506776124200175170ustar00rootroot00000000000000#include #include #include #include #include #include #include "bpftrace.h" #include "btf.h" #include "cxxdemangler/cxxdemangler.h" #include "dwarf_parser.h" #include "log.h" #include "probe_matcher.h" #include "scopeguard.h" #include "tracefs/tracefs.h" #include "util/bpf_progs.h" #include "util/paths.h" #include "util/strings.h" #include "util/symbols.h" #include "util/system.h" #include "util/wildcard.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 = util::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::ranges::none_of(tokens, has_parameter); std::string line; std::set matches; while (std::getline(symbol_stream, line, delim)) { if (!util::wildcard_match(line, tokens, start_wildcard, end_wildcard)) { if (demangle_symbols) { auto fun_line = line; auto prefix = fun_line.find(':') != std::string::npos ? util::erase_prefix(fun_line) + ":" : ""; if (util::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) { util::erase_parameter_list(match_line); } if (util::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; } // The two `has_btf_data` checks below for fentry/fexit/rawtracepoints // are more about ordering than system properties. // Initially, before BTF is loaded and we want to determine what modules to // load BTF from we check "/sys/kernel/tracing/available_filter_functions". // Then we check the BTF to filter out functions from that list that don't // have any BTF definitions. case ProbeType::rawtracepoint: { symbol_stream = get_raw_tracepoint_symbols(); break; } case ProbeType::fentry: case ProbeType::fexit: { if (target == "bpf") { symbol_stream = get_running_bpf_programs(); } else { symbol_stream = get_fentry_symbols(); } 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::iter: { if (!bpftrace_->has_btf_data()) break; std::string ret; auto iters = bpftrace_->btf_->get_all_iters(); for (const 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 (const auto& unit : TIME_UNITS) ret += unit + ":\n"; symbol_stream = std::make_unique(ret); break; } case ProbeType::special: case ProbeType::benchmark: 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 (const 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 (const auto& func_mod : bpftrace_->get_traceable_funcs()) { if (with_modules) { for (const 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_fentry_symbols() const { if (bpftrace_->btf_->has_data() && bpftrace_->btf_->modules_loaded()) return bpftrace_->btf_->get_all_funcs(); else { return get_symbols_from_traceable_funcs(true); } } std::unique_ptr ProbeMatcher::get_running_bpf_programs() const { std::string funcs; auto ids_and_syms = util::get_bpf_progs(); for (const auto& [id, symbol] : ids_and_syms) { funcs += "bpf:" + std::to_string(id) + ":" + symbol + "\n"; } return std::make_unique(funcs); } std::unique_ptr ProbeMatcher::get_raw_tracepoint_symbols() const { if (bpftrace_->btf_->has_data() && bpftrace_->btf_->modules_loaded()) { return bpftrace_->btf_->get_all_raw_tracepoints(); } else { std::string rts; for (const auto& rt_mod : bpftrace_->get_raw_tracepoints()) { for (const auto& mod : rt_mod.second) rts += mod + ":" + rt_mod.first + "\n"; } return std::make_unique(rts); } } std::unique_ptr ProbeMatcher::get_func_symbols_from_file( std::optional pid, const std::string& path) const { if (path.empty()) return std::make_unique(""); auto get_paths = [&]() -> Result> { if (path == "*") { if (pid.has_value()) { return util::get_mapped_paths_for_pid(*pid); } else { return util::get_mapped_paths_for_running_pids(); } } else if (path.find('*') != std::string::npos) { return util::resolve_binary_path(path, pid); } else { return std::vector({ path }); } }; auto real_paths = get_paths(); if (!real_paths) { // This will crash, but we need to plumb the ability to return errors // through these APIs. Right now we have no other choice. LOG(WARNING) << "Could not resolve binary path: " << real_paths.takeError(); return nullptr; } 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 (const auto& sym : syms) result += real_path + ":" + sym + "\n"; } return std::make_unique(result); } std::unique_ptr ProbeMatcher::get_symbols_from_usdt( std::optional pid, const std::string& target) const { std::string probes; usdt_probe_list usdt_probes; if (pid.has_value()) 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 = util::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 (const 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 (const 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 (const 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 (const auto& tracepoint : tracepoints) { auto event = tracepoint; auto category = util::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.empty()); while (getline(format_file, line)) { if (line.starts_with("\tfield:")) { 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 (const 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 (const auto& match : uprobes) { std::string fun = match; std::string path = util::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::rawtracepoint) param_lists = bpftrace_->btf_->get_rawtracepoint_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); else if (probe_type == ProbeType::kprobe) param_lists = bpftrace_->btf_->get_kprobes_params(matches); else if (probe_type == ProbeType::kretprobe) param_lists = bpftrace_->btf_->get_kretprobes_params(matches); } for (const auto& match : matches) { std::string match_print = match; if (ap->lang == "cpp") { std::string target = util::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::rawtracepoint: { search_input = attach_point.target + ":" + attach_point.func; break; } case ProbeType::iter: { search_input = attach_point.func; break; } case ProbeType::fentry: case ProbeType::fexit: { if (attach_point.target == "bpf") { search_input = "bpf:" + (attach_point.bpf_prog_id ? std::to_string(attach_point.bpf_prog_id) : "*") + ":" + attach_point.func; break; } [[fallthrough]]; } case ProbeType::special: case ProbeType::benchmark: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::tracepoint: { // 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().has_value()) { if (!target.empty()) { if (auto abs_target = util::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 (util::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 (util::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 (const auto& match : get_matches_in_set(search_input, structs)) std::cout << match << std::endl; } } // namespace bpftrace bpftrace-0.24.1/src/probe_matcher.h000066400000000000000000000126571506776124200171630ustar00rootroot00000000000000#pragma once #include #include #include "ast/ast.h" #include "btf.h" 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 = { { .path="alignment-faults", .alias="", .type=PERF_COUNT_SW_ALIGNMENT_FAULTS, .defaultp=1 }, { .path="bpf-output", .alias="", .type=PERF_COUNT_SW_BPF_OUTPUT, .defaultp=1 }, { .path="context-switches", .alias="cs", .type=PERF_COUNT_SW_CONTEXT_SWITCHES, .defaultp=1000 }, { .path="cpu-clock", .alias="cpu", .type=PERF_COUNT_SW_CPU_CLOCK, .defaultp=1000000 }, { .path="cpu-migrations", .alias="", .type=PERF_COUNT_SW_CPU_MIGRATIONS, .defaultp=1 }, { .path="dummy", .alias="", .type=PERF_COUNT_SW_DUMMY, .defaultp=1 }, { .path="emulation-faults", .alias="", .type=PERF_COUNT_SW_EMULATION_FAULTS, .defaultp=1 }, { .path="major-faults", .alias="", .type=PERF_COUNT_SW_PAGE_FAULTS_MAJ, .defaultp=1 }, { .path="minor-faults", .alias="", .type=PERF_COUNT_SW_PAGE_FAULTS_MIN, .defaultp=100 }, { .path="page-faults", .alias="faults", .type=PERF_COUNT_SW_PAGE_FAULTS, .defaultp=100 }, { .path="task-clock", .alias="", .type=PERF_COUNT_SW_TASK_CLOCK, .defaultp=1 }, }; const std::vector HW_PROBE_LIST = { { .path="backend-stalls", .alias="", .type=PERF_COUNT_HW_STALLED_CYCLES_BACKEND, .defaultp=1000000 }, { .path="branch-instructions", .alias="branches", .type=PERF_COUNT_HW_BRANCH_INSTRUCTIONS, .defaultp=100000 }, { .path="branch-misses", .alias="", .type=PERF_COUNT_HW_BRANCH_MISSES, .defaultp=100000 }, { .path="bus-cycles", .alias="", .type=PERF_COUNT_HW_BUS_CYCLES, .defaultp=100000 }, { .path="cache-misses", .alias="", .type=PERF_COUNT_HW_CACHE_MISSES, .defaultp=1000000 }, { .path="cache-references", .alias="", .type=PERF_COUNT_HW_CACHE_REFERENCES, .defaultp=1000000 }, { .path="cpu-cycles", .alias="cycles", .type=PERF_COUNT_HW_CPU_CYCLES, .defaultp=1000000 }, { .path="frontend-stalls", .alias="", .type=PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, .defaultp=1000000 }, { .path="instructions", .alias="", .type=PERF_COUNT_HW_INSTRUCTIONS, .defaultp=1000000 }, { .path="ref-cycles", .alias="", .type=PERF_COUNT_HW_REF_CPU_CYCLES, .defaultp=1000000 } }; // clang-format on const std::unordered_set TIME_UNITS = { "s", "ms", "us", "hz" }; const std::unordered_set SIGNALS = { "SIGUSR1" }; class BPFtrace; 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, 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( std::optional pid, const std::string &path) const; virtual std::unique_ptr get_symbols_from_usdt( std::optional pid, const std::string &target) const; virtual std::unique_ptr get_symbols_from_list( const std::vector &probes_list) const; virtual std::unique_ptr get_fentry_symbols() const; virtual std::unique_ptr get_running_bpf_programs() const; virtual std::unique_ptr get_raw_tracepoint_symbols() const; std::unique_ptr get_iter_symbols() 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.24.1/src/probe_types.cpp000066400000000000000000000046641506776124200172360ustar00rootroot00000000000000#include #include #include #include "probe_types.h" #include "util/strings.h" namespace bpftrace { std::ostream &operator<<(std::ostream &os, ProbeType type) { os << probetypeName(type); return os; } ProbeType probetype(const std::string &probeName) { ProbeType retType = ProbeType::invalid; const std::string lowerProbeName = util::to_lower(probeName); auto v = std::ranges::find_if(PROBE_LIST, [&lowerProbeName](const ProbeItem &p) { return (p.name == lowerProbeName || p.aliases.contains(lowerProbeName)); }); if (v != PROBE_LIST.end()) retType = v->type; return retType; } std::string expand_probe_name(const std::string &orig_name) { std::string expanded_name = util::to_lower(orig_name); auto v = std::ranges::find_if(PROBE_LIST, [&orig_name](const ProbeItem &p) { return (p.name == orig_name || p.aliases.contains(orig_name)); }); 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::benchmark: return "benchmark"; 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 } } // namespace bpftrace bpftrace-0.24.1/src/probe_types.h000066400000000000000000000104111506776124200166660ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include namespace bpftrace { enum class ProbeType { invalid, special, benchmark, 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 = {}, .type = ProbeType::special }, { .name = "end", .aliases = {}, .type = ProbeType::special }, { .name = "self", .aliases = {}, .type = ProbeType::special }, { .name = "bench", .aliases = {}, .type = ProbeType::benchmark }, { .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 &probeName); 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; uint64_t bpf_prog_id = 0; std::set funcs; bool is_session = false; 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); } }; } // namespace bpftrace bpftrace-0.24.1/src/procmon.cpp000066400000000000000000000040011506776124200163410ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "procmon.h" namespace bpftrace { #ifndef __NR_pidfd_open #define __NR_pidfd_open 434 #endif static std::system_error SYS_ERROR(std::string msg) { return { 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.24.1/src/procmon.h000066400000000000000000000014771506776124200160240ustar00rootroot00000000000000#pragma once #include namespace bpftrace { class ProcMonBase { public: ProcMonBase() = default; virtual ~ProcMonBase() = default; // Whether the process is still alive virtual bool is_alive() = 0; // pid of the process being monitored pid_t pid() { 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() override; private: int pidfd_ = -1; char proc_path_[32]; bool died_ = false; void setup(pid_t pid); }; } // namespace bpftrace bpftrace-0.24.1/src/required_resources.cpp000066400000000000000000000025221506776124200206040ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "required_resources.h" #include "util/io.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); util::Membuf mbuf(addr, addr + len); std::istream istream(&mbuf); cereal::BinaryInputArchive archive(istream); archive(*this); } std::ostream &operator<<(std::ostream &os, const RuntimeErrorInfo &info) { switch (info.error_id) { case RuntimeErrorId::HELPER_ERROR: { // Helper errors are handled separately in output os << ""; break; } case RuntimeErrorId::DIVIDE_BY_ZERO: { os << DIVIDE_BY_ZERO_MSG; break; } case RuntimeErrorId::ARRAY_ACCESS_OOB: { os << ARRAY_ACCESS_OOB_MSG; break; } } return os; } } // namespace bpftrace bpftrace-0.24.1/src/required_resources.h000066400000000000000000000155441506776124200202610ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include "ast/location.h" #include "format_string.h" #include "globalvars.h" #include "map_info.h" #include "struct.h" #include "types.h" #include "util/bpf_funcs.h" namespace bpftrace { class BPFtrace; static const auto DIVIDE_BY_ZERO_MSG = "Divide or modulo by 0 detected. This can lead to unexpected " "results. 1 is being used as the result."; static const auto ARRAY_ACCESS_OOB_MSG = "Array access out of bounds. This can lead to unexpected " "results."; enum class RuntimeErrorId { DIVIDE_BY_ZERO, HELPER_ERROR, ARRAY_ACCESS_OOB, }; enum class PrintfSeverity { NONE, ERROR, }; struct SourceLocation { std::string filename; int line; int column; std::string source_location; std::vector source_context; private: friend class cereal::access; template void serialize(Archive &archive) { archive(filename, line, column, source_location, source_context); } }; class SourceInfo { public: SourceInfo(const ast::Location &loc) { auto curr_loc = loc; while (curr_loc) { locations.emplace_back(curr_loc->filename(), curr_loc->line(), curr_loc->column(), curr_loc->source_location(), curr_loc->source_context()); auto &parent = curr_loc->parent; if (parent) { curr_loc = parent->loc; } else { break; } } } SourceInfo() = default; std::vector locations; private: friend class cereal::access; template void serialize(Archive &archive) { archive(locations); } }; class RuntimeErrorInfo : public SourceInfo { public: // This class effectively wraps a location, but preserves only the parts that // are needed to emit the error in a useful way. This is because it may be // serialized and used by a separate runtime. RuntimeErrorInfo(RuntimeErrorId error_id, libbpf::bpf_func_id func_id, const ast::Location &loc) : SourceInfo(loc), error_id(error_id), func_id(func_id) { } RuntimeErrorInfo(RuntimeErrorId error_id, const ast::Location &loc) : RuntimeErrorInfo(error_id, static_cast(-1), loc) { }; RuntimeErrorInfo() : error_id(RuntimeErrorId::HELPER_ERROR), func_id(static_cast(-1)) {}; RuntimeErrorId error_id; libbpf::bpf_func_id func_id; private: friend class cereal::access; template void serialize(Archive &archive) { archive(error_id, func_id); } }; std::ostream &operator<<(std::ostream &os, const RuntimeErrorInfo &info); // 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< std::tuple, PrintfSeverity, SourceInfo>> 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 runtime_error_info; std::vector probe_ids; // Map metadata std::map maps_info; globalvars::GlobalVars global_vars; bool using_skboutput = 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 benchmark_probes; std::vector signal_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, runtime_error_info, printf_args, probe_ids, maps_info, global_vars, using_skboutput, probes, signal_probes, special_probes, benchmark_probes); } }; } // namespace bpftrace bpftrace-0.24.1/src/run_bpftrace.cpp000066400000000000000000000106401506776124200173440ustar00rootroot00000000000000#include #include #include "log.h" #include "output/json.h" #include "output/text.h" #include "run_bpftrace.h" #include "types_format.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.contains(DebugStage::Libbpf)) 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, const std::string &output_file, const std::string &output_format, const ast::CDefinitions &c_definitions, BpfBytecode &bytecode, std::vector &&named_params) { int err; // Check for required features. if (!bpftrace.feature_->has_map_ringbuf()) { LOG(ERROR) << "Your kernel is too old and is missing the " "BPF_MAP_TYPE_RINGBUF, which bpftrace requires."; return 1; } // Process all arguments. auto named_param_vals = bpftrace.resources.global_vars.get_named_param_vals( named_params); if (!named_param_vals) { auto ok = handleErrors(std::move(named_param_vals), [&](const globalvars::UnknownParamError &uo_err) { LOG(ERROR) << uo_err.err(); auto hint = uo_err.hint(); if (!hint.empty()) { LOG(HINT) << hint; } }); if (!ok) { LOG(ERROR) << ok.takeError(); } return 1; } bytecode.update_global_vars(bpftrace, std::move(*named_param_vals)); // Create our output. 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 1; } os = &outputstream; } std::unique_ptr output; if (output_format.empty() || output_format == "text") { // Note that there are two parameters here: we leave the err output as // std::cerr, so this can be seen while running. 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 1; } // 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(*output, c_definitions, std::move(bytecode)); if (err) return err; // Indicate that we are done the main loop. output->end(); // We are now post-processing. If we receive another SIGINT, // handle it normally (exit) act.sa_handler = SIG_DFL; sigaction(SIGINT, &act, nullptr); // Print maps if needed (true by default). if (!dry_run) { if (bpftrace.run_benchmarks_) { output->benchmark_results(bpftrace.benchmark_results); } if (bpftrace.config_->print_maps_on_exit) { for (const auto &[_, map] : bpftrace.bytecode_.maps()) { if (!map.is_printable()) continue; auto res = format(bpftrace, c_definitions, map); if (!res) { std::cerr << "Error printing map: " << res.takeError(); continue; } output->map(map.name(), *res); } } } 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.24.1/src/run_bpftrace.h000066400000000000000000000007151506776124200170130ustar00rootroot00000000000000#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, const std::string &output_file, const std::string &output_format, const bpftrace::ast::CDefinitions &c_definitions, bpftrace::BpfBytecode &bytecode, std::vector &&named_params); bpftrace-0.24.1/src/scopeguard.h000066400000000000000000000012601506776124200164710ustar00rootroot00000000000000#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 /*unused*/, std::function fn) { return ScopeGuard(fn); } } // namespace bpftrace #define SCOPE_EXIT auto _ANON_VAR(SCOPE_EXIT_STATE) = ScopeGuardExit() + [&]() bpftrace-0.24.1/src/stdlib/000077500000000000000000000000001506776124200154465ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/CMakeLists.txt000066400000000000000000000050331506776124200202070ustar00rootroot00000000000000include(Embed) # The standard library is effectively a bunch of embedded files. # # The data layout is standardized by `stdlib.h`, but we generate # stdlib.c which includes the relevant generated headers. # # * Basically, everything here that is `*.c`, `*.h` and `*.bt` goes # into the `stdlib` embedded directory. Structure is preserved. # * Everything that is in `include` goes into `include` and is # available by default to extensions. # * The `libbpf` header are embedded in `include` also. file(GLOB_RECURSE STDLIB_SOURCES "*.c" "*.h" "*.bt") file(GLOB_RECURSE PUBLIC_HEADERS "include/*.h") file(GLOB_RECURSE BPF_HEADERS "${LIBBPF_INCLUDE_DIRS}/bpf/*.h") list(REMOVE_ITEM STDLIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/stdlib.h") list(REMOVE_ITEM STDLIB_SOURCES ${PUBLIC_HEADERS}) function(add_stdlib_source PREFIX FILENAME) # Generate our embedded header. get_filename_component(name "${FILENAME}" NAME) string(REPLACE "." "_" header_name "${name}") string(REPLACE "/" "_" rule_name "${PREFIX}/${header_name}") string(REPLACE "-" "_" rule_name "${rule_name}") embed( ${rule_name} ${FILENAME} OUTPUT ${PREFIX}/${header_name}.h VAR ${rule_name} ) # Add the file to the list of rules we need, and add the original # source file to the list of sources we need to depend on. list(APPEND STDLIB_TARGETS "${rule_name}") set(STDLIB_TARGETS "${STDLIB_TARGETS}" PARENT_SCOPE) # Set globals that are used to configure the template. set(STDLIB_INCLUDES "${STDLIB_INCLUDES} #include \"${PREFIX}/${header_name}.h\"" PARENT_SCOPE) set(STDLIB_FILES "${STDLIB_FILES} { \"${PREFIX}/${name}\", make_view(${rule_name}, sizeof(${rule_name})) }," PARENT_SCOPE) endfunction() foreach(filename ${STDLIB_SOURCES}) string(REGEX REPLACE "^${CMAKE_CURRENT_SOURCE_DIR}/" "" suffix "${filename}") get_filename_component(prefix "stdlib/${suffix}" DIRECTORY) add_stdlib_source("${prefix}" "${filename}") endforeach() foreach(filename ${PUBLIC_HEADERS}) string(REGEX REPLACE "^${CMAKE_CURRENT_SOURCE_DIR}/include/" "" suffix "${filename}") get_filename_component(prefix "include/${suffix}" DIRECTORY) add_stdlib_source("${prefix}" "${filename}") endforeach() foreach(filename ${BPF_HEADERS}) string(REGEX REPLACE "^${LIBBPF_INCLUDE_DIRS}/" "" suffix "${filename}") get_filename_component(prefix "include/${suffix}" DIRECTORY) add_stdlib_source("${prefix}" "${filename}") endforeach() configure_file(stdlib.cpp.in stdlib.cpp) add_library(stdlib STATIC ${CMAKE_CURRENT_BINARY_DIR}/stdlib.cpp) add_dependencies(stdlib ${STDLIB_TARGETS}) bpftrace-0.24.1/src/stdlib/base.bt000066400000000000000000001063341506776124200167160ustar00rootroot00000000000000// :variant void assert(bool condition, string message) // Simple assertion macro that will exit the entire script with an error code if the condition is not met. macro assert(cond, msg) { if (!cond) { errorf("assert failed: %s", msg); exit(1); } } // :function bswap // :variant uint8 bswap(uint8 n) // :variant uint16 bswap(uint16 n) // :variant uint32 bswap(uint32 n) // :variant 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`. // :function buf // :variant 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 // ``` // :function cat // :variant 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 // ``` // :variant uint64 cgroup() // ID of the cgroup the current process belongs to // // Only works with cgroupv2 // // This utilizes the BPF helper `get_current_cgroup_id` macro cgroup() { __builtin_cgroup } // :function cgroupid // :variant 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")); // } // ``` // :function cgroup_path // :variant 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 */ // } // ``` // :function clear // :variant void clear(map m) // // **async** // // Clear all keys/values from map `m`. // // ``` // interval:ms:100 { // @[rand % 10] = count(); // } // // interval:s:10 { // print(@); // clear(@); // } // ``` // :variant string comm() // Name of the current thread // // This utilizes the BPF helper `get_current_comm` macro comm() { __builtin_comm } // :variant uint32 cpid() // Child process ID, if bpftrace is invoked with `-c` macro cpid() { __builtin_cpid } // :variant uint32 cpu() // ID of the processor executing the BPF program // // BPF program, in this case, is the probe body // // This utilizes the BPF helper `raw_smp_processor_id` macro cpu() { __builtin_cpu } // :variant uint64 curtask() // Pointer to `struct task_struct` of the current task // // This utilizes the BPF helper `get_current_task` macro curtask() { __builtin_curtask } // :function delete // :variant bool delete(map m, mapkey k) // :deprecated_variant bool 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"));` // // If deletion fails (e.g. the key doesn’t exist) the function returns false (0). // Additionally, if the return value for `delete` is discarded, and deletion fails, you will get a warning. // // ``` // @a[1] = 1; // // delete(@a, 1); // no warning (the key exists) // // if (delete(@a, 2)) { // no warning (return value is used) // ... // } // // $did_delete = delete(@a, 2); // no warning (return value is used) // // delete(@a, 2); // warning (return value is discarded and the key doesn’t exist) // ``` // // 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]); // } // ``` // :variant uint64 elapsed() // ktime_get_ns - ktime_get_boot_ns macro elapsed() { __builtin_elapsed } // :function errorf // :variant void errorf(const string fmt, args...) // // **async** // // `errorf()` formats and prints data (similar to [`printf`](#printf)) as an error message with the source location. // // ``` // BEGIN { errorf("Something bad with args: %d, %s", 10, "arg2"); } // ``` // // Prints: // // ``` // EXPECT stdin:1:9-62: ERROR: Something bad with args: 10, arg2 // ``` // :function exit // :variant 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); // } // ``` // :variant string func() // Name of the current function being traced (kprobes,uprobes,fentry) macro func() { __builtin_func } // :function getopt // :variant bool getopt(string arg_name) // :variant string getopt(string arg_name, string default_value) // :variant int getopt(string arg_name, int default_value) // :variant bool getopt(string arg_name, bool default_value) // // Get the named command line argument/option e.g. // ``` // # bpftrace -e 'BEGIN { print(getopt("hello", 1)); }' -- --hello=5 // // ``` // // `getopt` defines the type of the argument by the default value’s type. // If no default type is provided, the option is treated like a boolean arg e.g. `getopt("hello")` would evaluate to `false` if `--hello` is not specified on the command line or `true` if `--hello` is passed or set to one of the following values: `true`, `1`. // Additionally, boolean args accept the following false values: `0`, `false` e.g. `--hello=false`. // If the arg is not set on the command line, the default value is used. // // ``` // # bpftrace -e 'BEGIN { print((getopt("aa", 10), getopt("bb", "hello"), getopt("cc"), getopt("dd", false))); }' -- --cc --bb=bye // // ``` // :variant uint64 gid() // Group ID of the current thread, as seen from the init namespace // // This utilizes the BPF helper `get_current_uid_gid` macro gid() { __builtin_gid } // :function has_key // :variant boolean 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 // } // ``` // :variant uint64 jiffies() // Jiffies of the kernel // // On 32-bit systems, using this builtin might be slower // // This utilizes the BPF helper `get_jiffies_64` macro jiffies() { __builtin_jiffies } // :function join // :variant void join(char *arr[], [char * sep = ' ']) // // **async** // // `join` joins a char * `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); // } // ``` // :function kaddr // :variant 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`. // :function kptr // :variant 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. // :function kstack // :variant 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 // */ // ``` // :function ksym // :variant 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 // */ // ``` // :function len // :variant int64 len(map m) // :variant int64 len(ustack stack) // :variant int64 len(kstack stack) // // For maps, return the number of elements in the map. // // For kstack/ustack, return the depth (measured in # of frames) of the call stack. // :function macaddr // :variant 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 // */ // ``` // :variant uint64 ncpus() // Number of CPUs macro ncpus() { __builtin_ncpus } // :function nsecs // :variant 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. // // :variant nsecs(monotonic) - nanosecond timestamp since boot, exclusive of time the system spent suspended (CLOCK_MONOTONIC) // :variant nsecs(boot) - nanoseconds since boot, inclusive of time the system spent suspended (CLOCK_BOOTTIME) // :variant nsecs(tai) - TAI timestamp in nanoseconds (CLOCK_TAI) // :variant 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 // * ... // */ // ``` // :function ntop // :variant inet ntop([int64 af, ] int addr) // :variant inet ntop([int64 af, ] char addr[4]) // :variant 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. // :variant uint32 numaid() // ID of the NUMA node executing the BPF program // // BPF program, in this case, is the probe body // // This utilizes the BPF helper `numa_node_id` macro numaid() { __builtin_numaid } // :function offsetof // :variant uint64 offsetof(STRUCT, FIELD[.SUBFIELD]) // :variant 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(); // } // ``` // :function override // :variant void override(uint64 rc) // // **unsafe** // // **Kernel** 4.16 // // This utilizes the BPF 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' // ``` // :function path // :variant char * path(struct path * path [, int32 size]) // // **Kernel** 5.10 // // This utilizes the BPF 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. // :function percpu_kaddr // :variant uint64 *percpu_kaddr(const string name) // :variant uint64 *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); // } // } // ``` // :function pid // :variant uint32 pid([curr_ns|init]) // :variant uint32 pid // // Returns the process ID of the current thread. // Defaults to `curr_ns`. // // * `pid(curr_ns)` - The process ID as seen from the PID namespace of bpftrace. // * `pid(init)` - The process ID as seen from the initial PID namespace. // :variant uint32 ppid(struct task_struct * task) // Get the pid of the parent process macro ppid(task) { task->real_parent->pid } // :function print // :variant void print(T val) // // **async** // :variant void print(T val) // :variant void print(@map) // :variant void print(@map, uint64 top) // :variant 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 // ``` // :function printf // :variant 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: // // | Specifier | Type | Description | // | --- | --- | --- | // | r | buffer | Hex-formatted string to print arbitrary binary content returned by the [buf](#buf) 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") // ``` // :variant string probe() // Name of the fully expanded probe // // For example: `kprobe:do_nanosleep` macro probe() { __builtin_probe } // :function pton // :variant char addr[4] pton(const string *addr_v4) // :variant 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. // :variant uint32 rand() // Get a pseudo random number // // This utilizes the BPF helper `get_prandom_u32` macro rand() { __builtin_rand } // :function reg // :variant 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. // :variant uint64 retval() // 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` macro retval() { __builtin_retval } // :function signal // :variant void signal(const string sig) // :variant void signal(uint32 signum) // // **unsafe** // // **Kernel** 5.3 // // This utilizes the BPF 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) // ``` // :function sizeof // :variant uint64 sizeof(TYPE) // :variant 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. // :function skboutput // :variant uint32 skboutput(const string path, struct sk_buff *skb, uint64 length, const uint64 offset) // // **Kernel** 5.5 // // This utilizes the BPF 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) // ``` // :function socket_cookie // :variant uint64 socket_cookie(struct sock *sk) // // This utilizes the BPF helper `bpf_get_socket_cookie` // // Retrieve the cookie (generated by the kernel) of the socket. // If no cookie has been set yet, generate a new cookie. Once generated, the socket cookie remains stable for the life of the socket. // // This function returns a `uint64` unique number on success, or 0 if **sk** is NULL. // // ``` // fentry:tcp_rcv_established // { // $cookie = socket_cookie(args->sk); // @psize[$cookie] = hist(args->skb->len); // } // ``` // // Prints: // // ``` // @psize[65551]: // [32, 64) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| // // @psize[504]: // [32, 64) 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| // [64, 128) 1 |@@@@@@@@@@@@@ | // [128, 256) 0 | | // [256, 512) 1 |@@@@@@@@@@@@@ | // [512, 1K) 0 | | // [1K, 2K) 0 | | // [2K, 4K) 1 |@@@@@@@@@@@@@ | // ``` // :function str // :variant string str(char * data [, uint32 length) // // This utilizes the BPF helpers `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 [Address-spaces](./language.md#address-spaces) for more information. // :function strcontains // :variant 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. // :function strerror // :variant 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)); // } // ``` // :function strftime // :variant 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: // // | Specifier | Description | // | --- | --- | // | `%f` | Microsecond as a decimal number, zero-padded on the left | // :function strncmp // :variant 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. // :function system // :variant 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`. // // ``` // Attached 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); // } // ``` // :function tid // :variant uint32 tid([curr_ns|init]) // :variant uint32 tid // // Returns the thread ID of the current thread. // Defaults to `curr_ns`. // // * `tid(curr_ns)` - The thread ID as seen from the PID namespace of bpftrace. // * `tid(init)` - The thread ID as seen from the initial PID namespace. // :function time // :variant 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. // :function uaddr // :variant T * uaddr(const string sym) // // **Supported probes** // // * uprobes // * uretprobes // * USDT // // ***Does not work with ASLR, see issue [#75](https://github.com/bpftrace/bpftrace/issues/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"))); // } // ``` // :variant uint64 uid() // User ID of the current thread, as seen from the init namespace // // This utilizes the BPF helper `get_current_uid_gid` macro uid() { __builtin_uid } // :function uptr // :variant 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. // :variant uint8 usermode() // Returns 1 if the current process is in user mode, 0 otherwise // // Currently only available on x86_64. macro usermode() { __builtin_usermode } // :variant string username() // Get the current username // // Often this is just "root" macro username() { __builtin_username } // :function ustack // :variant 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. // :function usym // :variant usym_t usym(uint64 * addr) // // **async** // // **Supported probes** // // * uprobes // * uretprobes // // Equal to [ksym](#ksym) 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 // */ // ``` // :function unwatch // :variant void unwatch(void * addr) // // **async** // // Removes a watchpoint // :function zero // :variant void zero(map m) // // **async** // // Set all values (for all keys) in the map to zero. bpftrace-0.24.1/src/stdlib/include/000077500000000000000000000000001506776124200170715ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/.clang-format000066400000000000000000000000501506776124200214370ustar00rootroot00000000000000DisableFormat: true SortIncludes: Never bpftrace-0.24.1/src/stdlib/include/__stddef_max_align_t.h000066400000000000000000000033521506776124200233560ustar00rootroot00000000000000/*===---- __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.24.1/src/stdlib/include/asm-generic/000077500000000000000000000000001506776124200212635ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm-generic/bitsperlong.h000066400000000000000000000021231506776124200237620ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI__ASM_GENERIC_BITS_PER_LONG #define _UAPI__ASM_GENERIC_BITS_PER_LONG #ifndef __BITS_PER_LONG /* * In order to keep safe and avoid regression, only unify uapi * bitsperlong.h for some archs which are using newer toolchains * that have the definitions of __CHAR_BIT__ and __SIZEOF_LONG__. * See the following link for more info: * https://lore.kernel.org/linux-arch/b9624545-2c80-49a1-ac3c-39264a591f7b@app.fastmail.com/ */ #if defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__) #define __BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) #else /* * There seems to be no way of detecting this automatically from user * space, so 64 bit architectures should override this in their * bitsperlong.h. In particular, an architecture that supports * both 32 and 64 bit user space must not rely on CONFIG_64BIT * to decide it, but rather check a compiler provided macro. */ #define __BITS_PER_LONG 32 #endif #endif #ifndef __BITS_PER_LONG_LONG #define __BITS_PER_LONG_LONG 64 #endif #endif /* _UAPI__ASM_GENERIC_BITS_PER_LONG */ bpftrace-0.24.1/src/stdlib/include/asm-generic/errno-base.h000066400000000000000000000031141506776124200234700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_BASE_H #define _ASM_GENERIC_ERRNO_BASE_H #define EPERM 1 /* Operation not permitted */ #define ENOENT 2 /* No such file or directory */ #define ESRCH 3 /* No such process */ #define EINTR 4 /* Interrupted system call */ #define EIO 5 /* I/O error */ #define ENXIO 6 /* No such device or address */ #define E2BIG 7 /* Argument list too long */ #define ENOEXEC 8 /* Exec format error */ #define EBADF 9 /* Bad file number */ #define ECHILD 10 /* No child processes */ #define EAGAIN 11 /* Try again */ #define ENOMEM 12 /* Out of memory */ #define EACCES 13 /* Permission denied */ #define EFAULT 14 /* Bad address */ #define ENOTBLK 15 /* Block device required */ #define EBUSY 16 /* Device or resource busy */ #define EEXIST 17 /* File exists */ #define EXDEV 18 /* Cross-device link */ #define ENODEV 19 /* No such device */ #define ENOTDIR 20 /* Not a directory */ #define EISDIR 21 /* Is a directory */ #define EINVAL 22 /* Invalid argument */ #define ENFILE 23 /* File table overflow */ #define EMFILE 24 /* Too many open files */ #define ENOTTY 25 /* Not a typewriter */ #define ETXTBSY 26 /* Text file busy */ #define EFBIG 27 /* File too large */ #define ENOSPC 28 /* No space left on device */ #define ESPIPE 29 /* Illegal seek */ #define EROFS 30 /* Read-only file system */ #define EMLINK 31 /* Too many links */ #define EPIPE 32 /* Broken pipe */ #define EDOM 33 /* Math argument out of domain of func */ #define ERANGE 34 /* Math result not representable */ #endif bpftrace-0.24.1/src/stdlib/include/asm-generic/errno.h000066400000000000000000000130201506776124200225550ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return * -ENOSYS if users try to call a syscall that doesn't exist. To keep * failures of syscalls that really do exist distinguishable from * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ #define ENOSYS 38 /* Invalid system call number */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif bpftrace-0.24.1/src/stdlib/include/asm-generic/fcntl.h000066400000000000000000000126731506776124200225530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_FCNTL_H #define _ASM_GENERIC_FCNTL_H #include /* * FMODE_EXEC is 0x20 * These cannot be used by userspace O_* until internal and external open * flags are split. * -Eric Paris */ /* * When introducing new O_* bits, please check its uniqueness in fcntl_init(). */ #define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 #define O_RDWR 00000002 #ifndef O_CREAT #define O_CREAT 00000100 /* not fcntl */ #endif #ifndef O_EXCL #define O_EXCL 00000200 /* not fcntl */ #endif #ifndef O_NOCTTY #define O_NOCTTY 00000400 /* not fcntl */ #endif #ifndef O_TRUNC #define O_TRUNC 00001000 /* not fcntl */ #endif #ifndef O_APPEND #define O_APPEND 00002000 #endif #ifndef O_NONBLOCK #define O_NONBLOCK 00004000 #endif #ifndef O_DSYNC #define O_DSYNC 00010000 /* used to be O_SYNC, see below */ #endif #ifndef FASYNC #define FASYNC 00020000 /* fcntl, for BSD compatibility */ #endif #ifndef O_DIRECT #define O_DIRECT 00040000 /* direct disk access hint */ #endif #ifndef O_LARGEFILE #define O_LARGEFILE 00100000 #endif #ifndef O_DIRECTORY #define O_DIRECTORY 00200000 /* must be a directory */ #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 /* don't follow links */ #endif #ifndef O_NOATIME #define O_NOATIME 01000000 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 /* set close_on_exec */ #endif /* * Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using * the O_SYNC flag. We continue to use the existing numerical value * for O_DSYNC semantics now, but using the correct symbolic name for it. * This new value is used to request true Posix O_SYNC semantics. It is * defined in this strange way to make sure applications compiled against * new headers get at least O_DSYNC semantics on older kernels. * * This has the nice side-effect that we can simply test for O_DSYNC * wherever we do not care if O_DSYNC or O_SYNC is used. * * Note: __O_SYNC must never be used directly. */ #ifndef O_SYNC #define __O_SYNC 04000000 #define O_SYNC (__O_SYNC|O_DSYNC) #endif #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef __O_TMPFILE #define __O_TMPFILE 020000000 #endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ #define F_GETFL 3 /* get file->f_flags */ #define F_SETFL 4 /* set file->f_flags */ #ifndef F_GETLK #define F_GETLK 5 #define F_SETLK 6 #define F_SETLKW 7 #endif #ifndef F_SETOWN #define F_SETOWN 8 /* for sockets. */ #define F_GETOWN 9 /* for sockets. */ #endif #ifndef F_SETSIG #define F_SETSIG 10 /* for sockets. */ #define F_GETSIG 11 /* for sockets. */ #endif #if __BITS_PER_LONG == 32 || defined(__KERNEL__) #ifndef F_GETLK64 #define F_GETLK64 12 /* using 'struct flock64' */ #define F_SETLK64 13 #define F_SETLKW64 14 #endif #endif /* __BITS_PER_LONG == 32 || defined(__KERNEL__) */ #ifndef F_SETOWN_EX #define F_SETOWN_EX 15 #define F_GETOWN_EX 16 #endif #ifndef F_GETOWNER_UIDS #define F_GETOWNER_UIDS 17 #endif /* * Open File Description Locks * * Usually record locks held by a process are released on *any* close and are * not inherited across a fork(). * * These cmd values will set locks that conflict with process-associated * record locks, but are "owned" by the open file description, not the * process. This means that they are inherited across fork() like BSD (flock) * locks, and they are only released automatically when the last reference to * the the open file against which they were acquired is put. */ #define F_OFD_GETLK 36 #define F_OFD_SETLK 37 #define F_OFD_SETLKW 38 #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 struct f_owner_ex { int type; __kernel_pid_t pid; }; /* for F_[GET|SET]FL */ #define FD_CLOEXEC 1 /* actually anything with low bit set goes */ /* for posix fcntl() and lockf() */ #ifndef F_RDLCK #define F_RDLCK 0 #define F_WRLCK 1 #define F_UNLCK 2 #endif /* for old implementation of bsd flock () */ #ifndef F_EXLCK #define F_EXLCK 4 /* or 3 */ #define F_SHLCK 8 /* or 4 */ #endif /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ #define LOCK_NB 4 /* or'd with one of the above to prevent blocking */ #define LOCK_UN 8 /* remove lock */ /* * LOCK_MAND support has been removed from the kernel. We leave the symbols * here to not break legacy builds, but these should not be used in new code. */ #define LOCK_MAND 32 /* This is a mandatory flock ... */ #define LOCK_READ 64 /* which allows concurrent read operations */ #define LOCK_WRITE 128 /* which allows concurrent write operations */ #define LOCK_RW 192 /* which allows concurrent read & write ops */ #define F_LINUX_SPECIFIC_BASE 1024 #ifndef HAVE_ARCH_STRUCT_FLOCK struct flock { short l_type; short l_whence; __kernel_off_t l_start; __kernel_off_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK_EXTRA_SYSID __ARCH_FLOCK_EXTRA_SYSID #endif #ifdef __ARCH_FLOCK_PAD __ARCH_FLOCK_PAD #endif }; struct flock64 { short l_type; short l_whence; __kernel_loff_t l_start; __kernel_loff_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK64_PAD __ARCH_FLOCK64_PAD #endif }; #endif /* HAVE_ARCH_STRUCT_FLOCK */ #endif /* _ASM_GENERIC_FCNTL_H */ bpftrace-0.24.1/src/stdlib/include/asm-generic/int-ll64.h000066400000000000000000000015571506776124200230150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * asm-generic/int-ll64.h * * Integer declarations for architectures which use "long long" * for 64-bit types. */ #ifndef _UAPI_ASM_GENERIC_INT_LL64_H #define _UAPI_ASM_GENERIC_INT_LL64_H #include #ifndef __ASSEMBLY__ /* * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the * header files exported to user space */ typedef __signed__ char __s8; typedef unsigned char __u8; typedef __signed__ short __s16; typedef unsigned short __u16; typedef __signed__ int __s32; typedef unsigned int __u32; #ifdef __GNUC__ __extension__ typedef __signed__ long long __s64; __extension__ typedef unsigned long long __u64; #else typedef __signed__ long long __s64; typedef unsigned long long __u64; #endif #endif /* __ASSEMBLY__ */ #endif /* _UAPI_ASM_GENERIC_INT_LL64_H */ bpftrace-0.24.1/src/stdlib/include/asm-generic/ioctl.h000066400000000000000000000067471506776124200225640ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_IOCTL_H #define _UAPI_ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 /* * Let any architecture override either of the following before * including this file. */ #ifndef _IOC_SIZEBITS # define _IOC_SIZEBITS 14 #endif #ifndef _IOC_DIRBITS # define _IOC_DIRBITS 2 #endif #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits, which any architecture can choose to override * before including this file. * * NOTE: _IOC_WRITE means userland is writing and kernel is * reading. _IOC_READ means userland is reading and kernel is writing. */ #ifndef _IOC_NONE # define _IOC_NONE 0U #endif #ifndef _IOC_WRITE # define _IOC_WRITE 1U #endif #ifndef _IOC_READ # define _IOC_READ 2U #endif #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) #ifndef __KERNEL__ #define _IOC_TYPECHECK(t) (sizeof(t)) #endif /* * Used to create numbers. * * NOTE: _IOW means userland is writing and kernel is reading. _IOR * means userland is reading and kernel is writing. */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOR_BAD(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),sizeof(argtype)) #define _IOW_BAD(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),sizeof(argtype)) #define _IOWR_BAD(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(argtype)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _UAPI_ASM_GENERIC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm-generic/posix_types.h000066400000000000000000000045421506776124200240270ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_GENERIC_POSIX_TYPES_H #define __ASM_GENERIC_POSIX_TYPES_H #include /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. * * First the types that are often defined in different ways across * architectures, so that you can override them. */ #ifndef __kernel_long_t typedef long __kernel_long_t; typedef unsigned long __kernel_ulong_t; #endif #ifndef __kernel_ino_t typedef __kernel_ulong_t __kernel_ino_t; #endif #ifndef __kernel_mode_t typedef unsigned int __kernel_mode_t; #endif #ifndef __kernel_pid_t typedef int __kernel_pid_t; #endif #ifndef __kernel_ipc_pid_t typedef int __kernel_ipc_pid_t; #endif #ifndef __kernel_uid_t typedef unsigned int __kernel_uid_t; typedef unsigned int __kernel_gid_t; #endif #ifndef __kernel_suseconds_t typedef __kernel_long_t __kernel_suseconds_t; #endif #ifndef __kernel_daddr_t typedef int __kernel_daddr_t; #endif #ifndef __kernel_uid32_t typedef unsigned int __kernel_uid32_t; typedef unsigned int __kernel_gid32_t; #endif #ifndef __kernel_old_uid_t typedef __kernel_uid_t __kernel_old_uid_t; typedef __kernel_gid_t __kernel_old_gid_t; #endif #ifndef __kernel_old_dev_t typedef unsigned int __kernel_old_dev_t; #endif /* * Most 32 bit architectures use "unsigned int" size_t, * and all 64 bit architectures use "unsigned long" size_t. */ #ifndef __kernel_size_t #if __BITS_PER_LONG != 64 typedef unsigned int __kernel_size_t; typedef int __kernel_ssize_t; typedef int __kernel_ptrdiff_t; #else typedef __kernel_ulong_t __kernel_size_t; typedef __kernel_long_t __kernel_ssize_t; typedef __kernel_long_t __kernel_ptrdiff_t; #endif #endif #ifndef __kernel_fsid_t typedef struct { int val[2]; } __kernel_fsid_t; #endif /* * anything below here should be completely generic */ typedef __kernel_long_t __kernel_off_t; typedef long long __kernel_loff_t; typedef __kernel_long_t __kernel_old_time_t; #ifndef __KERNEL__ typedef __kernel_long_t __kernel_time_t; #endif typedef long long __kernel_time64_t; typedef __kernel_long_t __kernel_clock_t; typedef int __kernel_timer_t; typedef int __kernel_clockid_t; typedef char * __kernel_caddr_t; typedef unsigned short __kernel_uid16_t; typedef unsigned short __kernel_gid16_t; #endif /* __ASM_GENERIC_POSIX_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm-generic/types.h000066400000000000000000000003701506776124200226000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_TYPES_H #define _UAPI_ASM_GENERIC_TYPES_H /* * int-ll64 is used everywhere now. */ #include #endif /* _UAPI_ASM_GENERIC_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/000077500000000000000000000000001506776124200176515ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/arm/000077500000000000000000000000001506776124200204305ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/arm/bitsperlong.h000066400000000000000000000021231506776124200231270ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI__ASM_GENERIC_BITS_PER_LONG #define _UAPI__ASM_GENERIC_BITS_PER_LONG #ifndef __BITS_PER_LONG /* * In order to keep safe and avoid regression, only unify uapi * bitsperlong.h for some archs which are using newer toolchains * that have the definitions of __CHAR_BIT__ and __SIZEOF_LONG__. * See the following link for more info: * https://lore.kernel.org/linux-arch/b9624545-2c80-49a1-ac3c-39264a591f7b@app.fastmail.com/ */ #if defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__) #define __BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) #else /* * There seems to be no way of detecting this automatically from user * space, so 64 bit architectures should override this in their * bitsperlong.h. In particular, an architecture that supports * both 32 and 64 bit user space must not rely on CONFIG_64BIT * to decide it, but rather check a compiler provided macro. */ #define __BITS_PER_LONG 32 #endif #endif #ifndef __BITS_PER_LONG_LONG #define __BITS_PER_LONG_LONG 64 #endif #endif /* _UAPI__ASM_GENERIC_BITS_PER_LONG */ bpftrace-0.24.1/src/stdlib/include/asm/arm/byteorder.h000066400000000000000000000013071506776124200226010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * arch/arm/include/asm/byteorder.h * * ARM Endian-ness. In little endian mode, the data bus is connected such * that byte accesses appear as: * 0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31 * and word accesses (data or instruction) appear as: * d0...d31 * * When in big endian mode, byte accesses appear as: * 0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7 * and word accesses (data or instruction) appear as: * d0...d31 */ #ifndef __ASM_ARM_BYTEORDER_H #define __ASM_ARM_BYTEORDER_H #ifdef __ARMEB__ #include #else #include #endif #endif bpftrace-0.24.1/src/stdlib/include/asm/arm/errno.h000066400000000000000000000130201506776124200217220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return * -ENOSYS if users try to call a syscall that doesn't exist. To keep * failures of syscalls that really do exist distinguishable from * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ #define ENOSYS 38 /* Invalid system call number */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif bpftrace-0.24.1/src/stdlib/include/asm/arm/fcntl.h000066400000000000000000000005431506776124200217110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ARM_FCNTL_H #define _ARM_FCNTL_H #define O_DIRECTORY 040000 /* must be a directory */ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ #define O_LARGEFILE 0400000 #include #endif bpftrace-0.24.1/src/stdlib/include/asm/arm/ioctl.h000066400000000000000000000067471506776124200217310ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_IOCTL_H #define _UAPI_ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 /* * Let any architecture override either of the following before * including this file. */ #ifndef _IOC_SIZEBITS # define _IOC_SIZEBITS 14 #endif #ifndef _IOC_DIRBITS # define _IOC_DIRBITS 2 #endif #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits, which any architecture can choose to override * before including this file. * * NOTE: _IOC_WRITE means userland is writing and kernel is * reading. _IOC_READ means userland is reading and kernel is writing. */ #ifndef _IOC_NONE # define _IOC_NONE 0U #endif #ifndef _IOC_WRITE # define _IOC_WRITE 1U #endif #ifndef _IOC_READ # define _IOC_READ 2U #endif #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) #ifndef __KERNEL__ #define _IOC_TYPECHECK(t) (sizeof(t)) #endif /* * Used to create numbers. * * NOTE: _IOW means userland is writing and kernel is reading. _IOR * means userland is reading and kernel is writing. */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOR_BAD(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),sizeof(argtype)) #define _IOW_BAD(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),sizeof(argtype)) #define _IOWR_BAD(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(argtype)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _UAPI_ASM_GENERIC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm/posix_types.h000066400000000000000000000020471506776124200231720ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * arch/arm/include/asm/posix_types.h * * Copyright (C) 1996-1998 Russell King. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Changelog: * 27-06-1996 RMK Created */ #ifndef __ARCH_ARM_POSIX_TYPES_H #define __ARCH_ARM_POSIX_TYPES_H /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. Also, we cannot * assume GCC is being used. */ typedef unsigned short __kernel_mode_t; #define __kernel_mode_t __kernel_mode_t typedef unsigned short __kernel_ipc_pid_t; #define __kernel_ipc_pid_t __kernel_ipc_pid_t typedef unsigned short __kernel_uid_t; typedef unsigned short __kernel_gid_t; #define __kernel_uid_t __kernel_uid_t typedef unsigned short __kernel_old_dev_t; #define __kernel_old_dev_t __kernel_old_dev_t #include #endif bpftrace-0.24.1/src/stdlib/include/asm/arm/rwonce.h000066400000000000000000000062051506776124200221010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm/swab.h000066400000000000000000000026641506776124200215450ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * arch/arm/include/asm/byteorder.h * * ARM Endian-ness. In little endian mode, the data bus is connected such * that byte accesses appear as: * 0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31 * and word accesses (data or instruction) appear as: * d0...d31 * * When in big endian mode, byte accesses appear as: * 0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7 * and word accesses (data or instruction) appear as: * d0...d31 */ #ifndef _UAPI__ASM_ARM_SWAB_H #define _UAPI__ASM_ARM_SWAB_H #include #include #if !defined(__STRICT_ANSI__) || defined(__KERNEL__) # define __SWAB_64_THRU_32__ #endif #if !defined(__KERNEL__) || __LINUX_ARM_ARCH__ < 6 static inline __attribute_const__ __u32 __arch_swab32(__u32 x) { __u32 t; #ifndef __thumb__ if (!__builtin_constant_p(x)) { /* * The compiler needs a bit of a hint here to always do the * right thing and not screw it up to different degrees * depending on the gcc version. */ asm ("eor\t%0, %1, %1, ror #16" : "=r" (t) : "r" (x)); } else #endif t = x ^ ((x << 16) | (x >> 16)); /* eor r1,r0,r0,ror #16 */ x = (x << 24) | (x >> 8); /* mov r0,r0,ror #8 */ t &= ~0x00FF0000; /* bic r1,r1,#0x00FF0000 */ x ^= (t >> 8); /* eor r0,r0,r1,lsr #8 */ return x; } #define __arch_swab32 __arch_swab32 #endif #endif /* _UAPI__ASM_ARM_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm/types.h000066400000000000000000000027051506776124200217510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_TYPES_H #define _UAPI_ASM_TYPES_H #include /* * The C99 types uintXX_t that are usually defined in 'stdint.h' are not as * unambiguous on ARM as you would expect. For the types below, there is a * difference on ARM between GCC built for bare metal ARM, GCC built for glibc * and the kernel itself, which results in build errors if you try to build with * -ffreestanding and include 'stdint.h' (such as when you include 'arm_neon.h' * in order to use NEON intrinsics) * * As the typedefs for these types in 'stdint.h' are based on builtin defines * supplied by GCC, we can tweak these to align with the kernel's idea of those * types, so 'linux/types.h' and 'stdint.h' can be safely included from the same * source file (provided that -ffreestanding is used). * * int32_t uint32_t uintptr_t * bare metal GCC long unsigned long unsigned int * glibc GCC int unsigned int unsigned int * kernel int unsigned int unsigned long */ #ifdef __INT32_TYPE__ #undef __INT32_TYPE__ #define __INT32_TYPE__ int #endif #ifdef __UINT32_TYPE__ #undef __UINT32_TYPE__ #define __UINT32_TYPE__ unsigned int #endif #ifdef __UINTPTR_TYPE__ #undef __UINTPTR_TYPE__ #define __UINTPTR_TYPE__ unsigned long #endif #endif /* _UAPI_ASM_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm64/000077500000000000000000000000001506776124200206025ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/arm64/bitsperlong.h000066400000000000000000000015111506776124200233010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * Copyright (C) 2012 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __ASM_BITSPERLONG_H #define __ASM_BITSPERLONG_H #define __BITS_PER_LONG 64 #include #endif /* __ASM_BITSPERLONG_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm64/byteorder.h000066400000000000000000000015671506776124200227630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * Copyright (C) 2012 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __ASM_BYTEORDER_H #define __ASM_BYTEORDER_H #ifdef __AARCH64EB__ #include #else #include #endif #endif /* __ASM_BYTEORDER_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm64/errno.h000066400000000000000000000130201506776124200220740ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return * -ENOSYS if users try to call a syscall that doesn't exist. To keep * failures of syscalls that really do exist distinguishable from * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ #define ENOSYS 38 /* Invalid system call number */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif bpftrace-0.24.1/src/stdlib/include/asm/arm64/fcntl.h000066400000000000000000000020251506776124200220600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * Copyright (C) 2012 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __ASM_FCNTL_H #define __ASM_FCNTL_H /* * Using our own definitions for AArch32 (compat) support. */ #define O_DIRECTORY 040000 /* must be a directory */ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ #define O_LARGEFILE 0400000 #include #endif bpftrace-0.24.1/src/stdlib/include/asm/arm64/ioctl.h000066400000000000000000000067471506776124200221030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_IOCTL_H #define _UAPI_ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 /* * Let any architecture override either of the following before * including this file. */ #ifndef _IOC_SIZEBITS # define _IOC_SIZEBITS 14 #endif #ifndef _IOC_DIRBITS # define _IOC_DIRBITS 2 #endif #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits, which any architecture can choose to override * before including this file. * * NOTE: _IOC_WRITE means userland is writing and kernel is * reading. _IOC_READ means userland is reading and kernel is writing. */ #ifndef _IOC_NONE # define _IOC_NONE 0U #endif #ifndef _IOC_WRITE # define _IOC_WRITE 1U #endif #ifndef _IOC_READ # define _IOC_READ 2U #endif #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) #ifndef __KERNEL__ #define _IOC_TYPECHECK(t) (sizeof(t)) #endif /* * Used to create numbers. * * NOTE: _IOW means userland is writing and kernel is reading. _IOR * means userland is reading and kernel is writing. */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOR_BAD(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),sizeof(argtype)) #define _IOW_BAD(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),sizeof(argtype)) #define _IOWR_BAD(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(argtype)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _UAPI_ASM_GENERIC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm64/posix_types.h000066400000000000000000000005051506776124200233410ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_POSIX_TYPES_H #define __ASM_POSIX_TYPES_H typedef unsigned short __kernel_old_uid_t; typedef unsigned short __kernel_old_gid_t; #define __kernel_old_uid_t __kernel_old_uid_t #include #endif /* __ASM_POSIX_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm64/rwonce.h000066400000000000000000000062051506776124200222530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm64/swab.h000066400000000000000000000007661506776124200217200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_SWAB_H #define _ASM_GENERIC_SWAB_H #include /* * 32 bit architectures typically (but not always) want to * set __SWAB_64_THRU_32__. In user space, this is only * valid if the compiler supports 64 bit data types. */ #if __BITS_PER_LONG == 32 #if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__) #define __SWAB_64_THRU_32__ #endif #endif #endif /* _ASM_GENERIC_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/arm64/types.h000066400000000000000000000003701506776124200221170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_TYPES_H #define _UAPI_ASM_GENERIC_TYPES_H /* * int-ll64 is used everywhere now. */ #include #endif /* _UAPI_ASM_GENERIC_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/000077500000000000000000000000001506776124200216255ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/loongarch/bitsperlong.h000066400000000000000000000021231506776124200243240ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI__ASM_GENERIC_BITS_PER_LONG #define _UAPI__ASM_GENERIC_BITS_PER_LONG #ifndef __BITS_PER_LONG /* * In order to keep safe and avoid regression, only unify uapi * bitsperlong.h for some archs which are using newer toolchains * that have the definitions of __CHAR_BIT__ and __SIZEOF_LONG__. * See the following link for more info: * https://lore.kernel.org/linux-arch/b9624545-2c80-49a1-ac3c-39264a591f7b@app.fastmail.com/ */ #if defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__) #define __BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) #else /* * There seems to be no way of detecting this automatically from user * space, so 64 bit architectures should override this in their * bitsperlong.h. In particular, an architecture that supports * both 32 and 64 bit user space must not rely on CONFIG_64BIT * to decide it, but rather check a compiler provided macro. */ #define __BITS_PER_LONG 32 #endif #endif #ifndef __BITS_PER_LONG_LONG #define __BITS_PER_LONG_LONG 64 #endif #endif /* _UAPI__ASM_GENERIC_BITS_PER_LONG */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/byteorder.h000066400000000000000000000005441506776124200240000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * Author: Hanlu Li * Huacai Chen * * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #ifndef _ASM_BYTEORDER_H #define _ASM_BYTEORDER_H #include #endif /* _ASM_BYTEORDER_H */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/errno.h000066400000000000000000000130201506776124200231170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return * -ENOSYS if users try to call a syscall that doesn't exist. To keep * failures of syscalls that really do exist distinguishable from * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ #define ENOSYS 38 /* Invalid system call number */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif bpftrace-0.24.1/src/stdlib/include/asm/loongarch/fcntl.h000066400000000000000000000126731506776124200231150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_FCNTL_H #define _ASM_GENERIC_FCNTL_H #include /* * FMODE_EXEC is 0x20 * These cannot be used by userspace O_* until internal and external open * flags are split. * -Eric Paris */ /* * When introducing new O_* bits, please check its uniqueness in fcntl_init(). */ #define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 #define O_RDWR 00000002 #ifndef O_CREAT #define O_CREAT 00000100 /* not fcntl */ #endif #ifndef O_EXCL #define O_EXCL 00000200 /* not fcntl */ #endif #ifndef O_NOCTTY #define O_NOCTTY 00000400 /* not fcntl */ #endif #ifndef O_TRUNC #define O_TRUNC 00001000 /* not fcntl */ #endif #ifndef O_APPEND #define O_APPEND 00002000 #endif #ifndef O_NONBLOCK #define O_NONBLOCK 00004000 #endif #ifndef O_DSYNC #define O_DSYNC 00010000 /* used to be O_SYNC, see below */ #endif #ifndef FASYNC #define FASYNC 00020000 /* fcntl, for BSD compatibility */ #endif #ifndef O_DIRECT #define O_DIRECT 00040000 /* direct disk access hint */ #endif #ifndef O_LARGEFILE #define O_LARGEFILE 00100000 #endif #ifndef O_DIRECTORY #define O_DIRECTORY 00200000 /* must be a directory */ #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 /* don't follow links */ #endif #ifndef O_NOATIME #define O_NOATIME 01000000 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 /* set close_on_exec */ #endif /* * Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using * the O_SYNC flag. We continue to use the existing numerical value * for O_DSYNC semantics now, but using the correct symbolic name for it. * This new value is used to request true Posix O_SYNC semantics. It is * defined in this strange way to make sure applications compiled against * new headers get at least O_DSYNC semantics on older kernels. * * This has the nice side-effect that we can simply test for O_DSYNC * wherever we do not care if O_DSYNC or O_SYNC is used. * * Note: __O_SYNC must never be used directly. */ #ifndef O_SYNC #define __O_SYNC 04000000 #define O_SYNC (__O_SYNC|O_DSYNC) #endif #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef __O_TMPFILE #define __O_TMPFILE 020000000 #endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ #define F_GETFL 3 /* get file->f_flags */ #define F_SETFL 4 /* set file->f_flags */ #ifndef F_GETLK #define F_GETLK 5 #define F_SETLK 6 #define F_SETLKW 7 #endif #ifndef F_SETOWN #define F_SETOWN 8 /* for sockets. */ #define F_GETOWN 9 /* for sockets. */ #endif #ifndef F_SETSIG #define F_SETSIG 10 /* for sockets. */ #define F_GETSIG 11 /* for sockets. */ #endif #if __BITS_PER_LONG == 32 || defined(__KERNEL__) #ifndef F_GETLK64 #define F_GETLK64 12 /* using 'struct flock64' */ #define F_SETLK64 13 #define F_SETLKW64 14 #endif #endif /* __BITS_PER_LONG == 32 || defined(__KERNEL__) */ #ifndef F_SETOWN_EX #define F_SETOWN_EX 15 #define F_GETOWN_EX 16 #endif #ifndef F_GETOWNER_UIDS #define F_GETOWNER_UIDS 17 #endif /* * Open File Description Locks * * Usually record locks held by a process are released on *any* close and are * not inherited across a fork(). * * These cmd values will set locks that conflict with process-associated * record locks, but are "owned" by the open file description, not the * process. This means that they are inherited across fork() like BSD (flock) * locks, and they are only released automatically when the last reference to * the the open file against which they were acquired is put. */ #define F_OFD_GETLK 36 #define F_OFD_SETLK 37 #define F_OFD_SETLKW 38 #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 struct f_owner_ex { int type; __kernel_pid_t pid; }; /* for F_[GET|SET]FL */ #define FD_CLOEXEC 1 /* actually anything with low bit set goes */ /* for posix fcntl() and lockf() */ #ifndef F_RDLCK #define F_RDLCK 0 #define F_WRLCK 1 #define F_UNLCK 2 #endif /* for old implementation of bsd flock () */ #ifndef F_EXLCK #define F_EXLCK 4 /* or 3 */ #define F_SHLCK 8 /* or 4 */ #endif /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ #define LOCK_NB 4 /* or'd with one of the above to prevent blocking */ #define LOCK_UN 8 /* remove lock */ /* * LOCK_MAND support has been removed from the kernel. We leave the symbols * here to not break legacy builds, but these should not be used in new code. */ #define LOCK_MAND 32 /* This is a mandatory flock ... */ #define LOCK_READ 64 /* which allows concurrent read operations */ #define LOCK_WRITE 128 /* which allows concurrent write operations */ #define LOCK_RW 192 /* which allows concurrent read & write ops */ #define F_LINUX_SPECIFIC_BASE 1024 #ifndef HAVE_ARCH_STRUCT_FLOCK struct flock { short l_type; short l_whence; __kernel_off_t l_start; __kernel_off_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK_EXTRA_SYSID __ARCH_FLOCK_EXTRA_SYSID #endif #ifdef __ARCH_FLOCK_PAD __ARCH_FLOCK_PAD #endif }; struct flock64 { short l_type; short l_whence; __kernel_loff_t l_start; __kernel_loff_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK64_PAD __ARCH_FLOCK64_PAD #endif }; #endif /* HAVE_ARCH_STRUCT_FLOCK */ #endif /* _ASM_GENERIC_FCNTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/ioctl.h000066400000000000000000000067471506776124200231260ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_IOCTL_H #define _UAPI_ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 /* * Let any architecture override either of the following before * including this file. */ #ifndef _IOC_SIZEBITS # define _IOC_SIZEBITS 14 #endif #ifndef _IOC_DIRBITS # define _IOC_DIRBITS 2 #endif #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits, which any architecture can choose to override * before including this file. * * NOTE: _IOC_WRITE means userland is writing and kernel is * reading. _IOC_READ means userland is reading and kernel is writing. */ #ifndef _IOC_NONE # define _IOC_NONE 0U #endif #ifndef _IOC_WRITE # define _IOC_WRITE 1U #endif #ifndef _IOC_READ # define _IOC_READ 2U #endif #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) #ifndef __KERNEL__ #define _IOC_TYPECHECK(t) (sizeof(t)) #endif /* * Used to create numbers. * * NOTE: _IOW means userland is writing and kernel is reading. _IOR * means userland is reading and kernel is writing. */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOR_BAD(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),sizeof(argtype)) #define _IOW_BAD(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),sizeof(argtype)) #define _IOWR_BAD(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(argtype)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _UAPI_ASM_GENERIC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/posix_types.h000066400000000000000000000045421506776124200243710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_GENERIC_POSIX_TYPES_H #define __ASM_GENERIC_POSIX_TYPES_H #include /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. * * First the types that are often defined in different ways across * architectures, so that you can override them. */ #ifndef __kernel_long_t typedef long __kernel_long_t; typedef unsigned long __kernel_ulong_t; #endif #ifndef __kernel_ino_t typedef __kernel_ulong_t __kernel_ino_t; #endif #ifndef __kernel_mode_t typedef unsigned int __kernel_mode_t; #endif #ifndef __kernel_pid_t typedef int __kernel_pid_t; #endif #ifndef __kernel_ipc_pid_t typedef int __kernel_ipc_pid_t; #endif #ifndef __kernel_uid_t typedef unsigned int __kernel_uid_t; typedef unsigned int __kernel_gid_t; #endif #ifndef __kernel_suseconds_t typedef __kernel_long_t __kernel_suseconds_t; #endif #ifndef __kernel_daddr_t typedef int __kernel_daddr_t; #endif #ifndef __kernel_uid32_t typedef unsigned int __kernel_uid32_t; typedef unsigned int __kernel_gid32_t; #endif #ifndef __kernel_old_uid_t typedef __kernel_uid_t __kernel_old_uid_t; typedef __kernel_gid_t __kernel_old_gid_t; #endif #ifndef __kernel_old_dev_t typedef unsigned int __kernel_old_dev_t; #endif /* * Most 32 bit architectures use "unsigned int" size_t, * and all 64 bit architectures use "unsigned long" size_t. */ #ifndef __kernel_size_t #if __BITS_PER_LONG != 64 typedef unsigned int __kernel_size_t; typedef int __kernel_ssize_t; typedef int __kernel_ptrdiff_t; #else typedef __kernel_ulong_t __kernel_size_t; typedef __kernel_long_t __kernel_ssize_t; typedef __kernel_long_t __kernel_ptrdiff_t; #endif #endif #ifndef __kernel_fsid_t typedef struct { int val[2]; } __kernel_fsid_t; #endif /* * anything below here should be completely generic */ typedef __kernel_long_t __kernel_off_t; typedef long long __kernel_loff_t; typedef __kernel_long_t __kernel_old_time_t; #ifndef __KERNEL__ typedef __kernel_long_t __kernel_time_t; #endif typedef long long __kernel_time64_t; typedef __kernel_long_t __kernel_clock_t; typedef int __kernel_timer_t; typedef int __kernel_clockid_t; typedef char * __kernel_caddr_t; typedef unsigned short __kernel_uid16_t; typedef unsigned short __kernel_gid16_t; #endif /* __ASM_GENERIC_POSIX_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/rwonce.h000066400000000000000000000062051506776124200232760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/swab.h000066400000000000000000000007661506776124200227430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_SWAB_H #define _ASM_GENERIC_SWAB_H #include /* * 32 bit architectures typically (but not always) want to * set __SWAB_64_THRU_32__. In user space, this is only * valid if the compiler supports 64 bit data types. */ #if __BITS_PER_LONG == 32 #if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__) #define __SWAB_64_THRU_32__ #endif #endif #endif /* _ASM_GENERIC_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/loongarch/types.h000066400000000000000000000003701506776124200231420ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_TYPES_H #define _UAPI_ASM_GENERIC_TYPES_H /* * int-ll64 is used everywhere now. */ #include #endif /* _UAPI_ASM_GENERIC_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/000077500000000000000000000000001506776124200206215ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/mips/bitsperlong.h000066400000000000000000000003641506776124200233250ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_MIPS_BITSPERLONG_H #define __ASM_MIPS_BITSPERLONG_H #define __BITS_PER_LONG _MIPS_SZLONG #include #endif /* __ASM_MIPS_BITSPERLONG_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/byteorder.h000066400000000000000000000011041506776124200227650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1996, 99, 2003 by Ralf Baechle */ #ifndef _ASM_BYTEORDER_H #define _ASM_BYTEORDER_H #if defined(__MIPSEB__) #include #elif defined(__MIPSEL__) #include #else # error "MIPS, but neither __MIPSEB__, nor __MIPSEL__???" #endif #endif /* _ASM_BYTEORDER_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/errno.h000066400000000000000000000132401506776124200221170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1995, 1999, 2001, 2002 by Ralf Baechle */ #ifndef _UAPI_ASM_ERRNO_H #define _UAPI_ASM_ERRNO_H /* * These error numbers are intended to be MIPS ABI compatible */ #include #define ENOMSG 35 /* No message of desired type */ #define EIDRM 36 /* Identifier removed */ #define ECHRNG 37 /* Channel number out of range */ #define EL2NSYNC 38 /* Level 2 not synchronized */ #define EL3HLT 39 /* Level 3 halted */ #define EL3RST 40 /* Level 3 reset */ #define ELNRNG 41 /* Link number out of range */ #define EUNATCH 42 /* Protocol driver not attached */ #define ENOCSI 43 /* No CSI structure available */ #define EL2HLT 44 /* Level 2 halted */ #define EDEADLK 45 /* Resource deadlock would occur */ #define ENOLCK 46 /* No record locks available */ #define EBADE 50 /* Invalid exchange */ #define EBADR 51 /* Invalid request descriptor */ #define EXFULL 52 /* Exchange full */ #define ENOANO 53 /* No anode */ #define EBADRQC 54 /* Invalid request code */ #define EBADSLT 55 /* Invalid slot */ #define EDEADLOCK 56 /* File locking deadlock error */ #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EDOTDOT 73 /* RFS specific error */ #define EMULTIHOP 74 /* Multihop attempted */ #define EBADMSG 77 /* Not a data message */ #define ENAMETOOLONG 78 /* File name too long */ #define EOVERFLOW 79 /* Value too large for defined data type */ #define ENOTUNIQ 80 /* Name not unique on network */ #define EBADFD 81 /* File descriptor in bad state */ #define EREMCHG 82 /* Remote address changed */ #define ELIBACC 83 /* Can not access a needed shared library */ #define ELIBBAD 84 /* Accessing a corrupted shared library */ #define ELIBSCN 85 /* .lib section in a.out corrupted */ #define ELIBMAX 86 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 87 /* Cannot exec a shared library directly */ #define EILSEQ 88 /* Illegal byte sequence */ #define ENOSYS 89 /* Function not implemented */ #define ELOOP 90 /* Too many symbolic links encountered */ #define ERESTART 91 /* Interrupted system call should be restarted */ #define ESTRPIPE 92 /* Streams pipe error */ #define ENOTEMPTY 93 /* Directory not empty */ #define EUSERS 94 /* Too many users */ #define ENOTSOCK 95 /* Socket operation on non-socket */ #define EDESTADDRREQ 96 /* Destination address required */ #define EMSGSIZE 97 /* Message too long */ #define EPROTOTYPE 98 /* Protocol wrong type for socket */ #define ENOPROTOOPT 99 /* Protocol not available */ #define EPROTONOSUPPORT 120 /* Protocol not supported */ #define ESOCKTNOSUPPORT 121 /* Socket type not supported */ #define EOPNOTSUPP 122 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 123 /* Protocol family not supported */ #define EAFNOSUPPORT 124 /* Address family not supported by protocol */ #define EADDRINUSE 125 /* Address already in use */ #define EADDRNOTAVAIL 126 /* Cannot assign requested address */ #define ENETDOWN 127 /* Network is down */ #define ENETUNREACH 128 /* Network is unreachable */ #define ENETRESET 129 /* Network dropped connection because of reset */ #define ECONNABORTED 130 /* Software caused connection abort */ #define ECONNRESET 131 /* Connection reset by peer */ #define ENOBUFS 132 /* No buffer space available */ #define EISCONN 133 /* Transport endpoint is already connected */ #define ENOTCONN 134 /* Transport endpoint is not connected */ #define EUCLEAN 135 /* Structure needs cleaning */ #define ENOTNAM 137 /* Not a XENIX named type file */ #define ENAVAIL 138 /* No XENIX semaphores available */ #define EISNAM 139 /* Is a named type file */ #define EREMOTEIO 140 /* Remote I/O error */ #define EINIT 141 /* Reserved */ #define EREMDEV 142 /* Error 142 */ #define ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 144 /* Too many references: cannot splice */ #define ETIMEDOUT 145 /* Connection timed out */ #define ECONNREFUSED 146 /* Connection refused */ #define EHOSTDOWN 147 /* Host is down */ #define EHOSTUNREACH 148 /* No route to host */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define EALREADY 149 /* Operation already in progress */ #define EINPROGRESS 150 /* Operation now in progress */ #define ESTALE 151 /* Stale file handle */ #define ECANCELED 158 /* AIO operation canceled */ /* * These error are Linux extensions. */ #define ENOMEDIUM 159 /* No medium found */ #define EMEDIUMTYPE 160 /* Wrong medium type */ #define ENOKEY 161 /* Required key not available */ #define EKEYEXPIRED 162 /* Key has expired */ #define EKEYREVOKED 163 /* Key has been revoked */ #define EKEYREJECTED 164 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 165 /* Owner died */ #define ENOTRECOVERABLE 166 /* State not recoverable */ #define ERFKILL 167 /* Operation not possible due to RF-kill */ #define EHWPOISON 168 /* Memory page has hardware error */ #define EDQUOT 1133 /* Quota exceeded */ #endif /* _UAPI_ASM_ERRNO_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/fcntl.h000066400000000000000000000037541506776124200221110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1995, 96, 97, 98, 99, 2003, 05 Ralf Baechle */ #ifndef _UAPI_ASM_FCNTL_H #define _UAPI_ASM_FCNTL_H #include #define O_APPEND 0x0008 #define O_DSYNC 0x0010 /* used to be O_SYNC, see below */ #define O_NONBLOCK 0x0080 #define O_CREAT 0x0100 /* not fcntl */ #define O_TRUNC 0x0200 /* not fcntl */ #define O_EXCL 0x0400 /* not fcntl */ #define O_NOCTTY 0x0800 /* not fcntl */ #define FASYNC 0x1000 /* fcntl, for BSD compatibility */ #define O_LARGEFILE 0x2000 /* allow large file opens */ /* * Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using * the O_SYNC flag. We continue to use the existing numerical value * for O_DSYNC semantics now, but using the correct symbolic name for it. * This new value is used to request true Posix O_SYNC semantics. It is * defined in this strange way to make sure applications compiled against * new headers get at least O_DSYNC semantics on older kernels. * * This has the nice side-effect that we can simply test for O_DSYNC * wherever we do not care if O_DSYNC or O_SYNC is used. * * Note: __O_SYNC must never be used directly. */ #define __O_SYNC 0x4000 #define O_SYNC (__O_SYNC|O_DSYNC) #define O_DIRECT 0x8000 /* direct disk access hint */ #define F_GETLK 14 #define F_SETLK 6 #define F_SETLKW 7 #define F_SETOWN 24 /* for sockets. */ #define F_GETOWN 23 /* for sockets. */ #if __BITS_PER_LONG == 32 || defined(__KERNEL__) #define F_GETLK64 33 /* using 'struct flock64' */ #define F_SETLK64 34 #define F_SETLKW64 35 #endif /* __BITS_PER_LONG == 32 || defined(__KERNEL__) */ #if _MIPS_SIM != _MIPS_SIM_ABI64 #define __ARCH_FLOCK_EXTRA_SYSID long l_sysid; #define __ARCH_FLOCK_PAD long pad[4]; #endif #include #endif /* _UAPI_ASM_FCNTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/ioctl.h000066400000000000000000000014151506776124200221050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1995, 96, 99, 2001 Ralf Baechle * Copyright (C) 2009 Wind River Systems * Written by Ralf Baechle */ #ifndef __ASM_IOCTL_H #define __ASM_IOCTL_H #define _IOC_SIZEBITS 13 #define _IOC_DIRBITS 3 /* * Direction bits _IOC_NONE could be 0, but OSF/1 gives it a bit. * And this turns out useful to catch old ioctl numbers in header * files for us. */ #define _IOC_NONE 1U #define _IOC_READ 2U #define _IOC_WRITE 4U #include #endif /* __ASM_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/posix_types.h000066400000000000000000000013651506776124200233650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1996, 97, 98, 99, 2000 by Ralf Baechle * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */ #ifndef _ASM_POSIX_TYPES_H #define _ASM_POSIX_TYPES_H #include /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. Also, we cannot * assume GCC is being used. */ typedef long __kernel_daddr_t; #define __kernel_daddr_t __kernel_daddr_t #include #endif /* _ASM_POSIX_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/rwonce.h000066400000000000000000000062051506776124200222720ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/swab.h000066400000000000000000000027741506776124200217400ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1996, 99, 2003 by Ralf Baechle */ #ifndef _ASM_SWAB_H #define _ASM_SWAB_H #include #include #define __SWAB_64_THRU_32__ #if !defined(__mips16) && \ ((defined(__mips_isa_rev) && (__mips_isa_rev >= 2)) || \ defined(_MIPS_ARCH_LOONGSON3A)) static inline __attribute_const__ __u16 __arch_swab16(__u16 x) { __asm__( " .set push \n" " .set arch=mips32r2 \n" " wsbh %0, %1 \n" " .set pop \n" : "=r" (x) : "r" (x)); return x; } #define __arch_swab16 __arch_swab16 static inline __attribute_const__ __u32 __arch_swab32(__u32 x) { __asm__( " .set push \n" " .set arch=mips32r2 \n" " wsbh %0, %1 \n" " rotr %0, %0, 16 \n" " .set pop \n" : "=r" (x) : "r" (x)); return x; } #define __arch_swab32 __arch_swab32 /* * Having already checked for MIPS R2, enable the optimized version for * 64-bit kernel on r2 CPUs. */ #ifdef __mips64 static inline __attribute_const__ __u64 __arch_swab64(__u64 x) { __asm__( " .set push \n" " .set arch=mips64r2 \n" " dsbh %0, %1 \n" " dshd %0, %0 \n" " .set pop \n" : "=r" (x) : "r" (x)); return x; } #define __arch_swab64 __arch_swab64 #endif /* __mips64 */ #endif /* (not __mips16) and (MIPS R2 or newer or Loongson 3A) */ #endif /* _ASM_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/mips/types.h000066400000000000000000000016321506776124200221400ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1994, 1995, 1996, 1999 by Ralf Baechle * Copyright (C) 2008 Wind River Systems, * written by Ralf Baechle * Copyright (C) 1999 Silicon Graphics, Inc. */ #ifndef _UAPI_ASM_TYPES_H #define _UAPI_ASM_TYPES_H /* * We don't use int-l64.h for the kernel anymore but still use it for * userspace to avoid code changes. * * However, some user programs (e.g. perf) may not want this. They can * flag __SANE_USERSPACE_TYPES__ to get int-ll64.h here. */ #ifndef __KERNEL__ # if _MIPS_SZLONG == 64 && !defined(__SANE_USERSPACE_TYPES__) # include # else # include # endif #endif #endif /* _UAPI_ASM_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/000077500000000000000000000000001506776124200213305ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/powerpc/bitsperlong.h000066400000000000000000000004701506776124200240320ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_POWERPC_BITSPERLONG_H #define __ASM_POWERPC_BITSPERLONG_H #if defined(__powerpc64__) # define __BITS_PER_LONG 64 #else # define __BITS_PER_LONG 32 #endif #include #endif /* __ASM_POWERPC_BITSPERLONG_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/byteorder.h000066400000000000000000000010461506776124200235010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ #ifndef _ASM_POWERPC_BYTEORDER_H #define _ASM_POWERPC_BYTEORDER_H /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #ifdef __LITTLE_ENDIAN__ #include #else #include #endif #endif /* _ASM_POWERPC_BYTEORDER_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/errno.h000066400000000000000000000004261506776124200226300ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_POWERPC_ERRNO_H #define _ASM_POWERPC_ERRNO_H #undef EDEADLOCK #include #undef EDEADLOCK #define EDEADLOCK 58 /* File locking deadlock error */ #endif /* _ASM_POWERPC_ERRNO_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/fcntl.h000066400000000000000000000005571506776124200226160ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_FCNTL_H #define _ASM_FCNTL_H #define O_DIRECTORY 040000 /* must be a directory */ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_LARGEFILE 0200000 #define O_DIRECT 0400000 /* direct disk access hint */ #include #endif /* _ASM_FCNTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/ioctl.h000066400000000000000000000004561506776124200226200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_POWERPC_IOCTL_H #define _ASM_POWERPC_IOCTL_H #define _IOC_SIZEBITS 13 #define _IOC_DIRBITS 3 #define _IOC_NONE 1U #define _IOC_READ 2U #define _IOC_WRITE 4U #include #endif /* _ASM_POWERPC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/posix_types.h000066400000000000000000000011221506776124200240630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_POWERPC_POSIX_TYPES_H #define _ASM_POWERPC_POSIX_TYPES_H /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. Also, we cannot * assume GCC is being used. */ #ifdef __powerpc64__ typedef unsigned long __kernel_old_dev_t; #define __kernel_old_dev_t __kernel_old_dev_t #else typedef short __kernel_ipc_pid_t; #define __kernel_ipc_pid_t __kernel_ipc_pid_t #endif #include #endif /* _ASM_POWERPC_POSIX_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/rwonce.h000066400000000000000000000062051506776124200230010ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/swab.h000066400000000000000000000011321506776124200224320ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #ifndef _UAPI_ASM_POWERPC_SWAB_H #define _UAPI_ASM_POWERPC_SWAB_H #include #include #ifdef __GNUC__ #ifndef __powerpc64__ #define __SWAB_64_THRU_32__ #endif /* __powerpc64__ */ #endif /* __GNUC__ */ #endif /* _UAPI_ASM_POWERPC_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/powerpc/types.h000066400000000000000000000024471506776124200226540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * This file is never included by application software unless * explicitly requested (e.g., via linux/types.h) in which case the * application is Linux specific so (user-) name space pollution is * not a major issue. However, for interoperability, libraries still * need to be careful to avoid a name clashes. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #ifndef _UAPI_ASM_POWERPC_TYPES_H #define _UAPI_ASM_POWERPC_TYPES_H /* * This is here because we used to use l64 for 64bit powerpc * and we don't want to impact user mode with our change to ll64 * in the kernel. * * However, some user programs are fine with this. They can * flag __SANE_USERSPACE_TYPES__ to get int-ll64.h here. */ #if !defined(__SANE_USERSPACE_TYPES__) && defined(__powerpc64__) && !defined(__KERNEL__) # include #else # include #endif #ifndef __ASSEMBLY__ typedef struct { __u32 u[4]; } __attribute__((aligned(16))) __vector128; #endif /* __ASSEMBLY__ */ #endif /* _UAPI_ASM_POWERPC_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/000077500000000000000000000000001506776124200207775ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/riscv/bitsperlong.h000066400000000000000000000005711506776124200235030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ /* * Copyright (C) 2012 ARM Ltd. * Copyright (C) 2015 Regents of the University of California */ #ifndef _UAPI_ASM_RISCV_BITSPERLONG_H #define _UAPI_ASM_RISCV_BITSPERLONG_H #define __BITS_PER_LONG (__SIZEOF_POINTER__ * 8) #include #endif /* _UAPI_ASM_RISCV_BITSPERLONG_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/byteorder.h000066400000000000000000000005071506776124200231510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ /* * Copyright (C) 2012 ARM Ltd. * Copyright (C) 2015 Regents of the University of California */ #ifndef _UAPI_ASM_RISCV_BYTEORDER_H #define _UAPI_ASM_RISCV_BYTEORDER_H #include #endif /* _UAPI_ASM_RISCV_BYTEORDER_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/errno.h000066400000000000000000000130201506776124200222710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return * -ENOSYS if users try to call a syscall that doesn't exist. To keep * failures of syscalls that really do exist distinguishable from * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ #define ENOSYS 38 /* Invalid system call number */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif bpftrace-0.24.1/src/stdlib/include/asm/riscv/fcntl.h000066400000000000000000000126731506776124200222670ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_FCNTL_H #define _ASM_GENERIC_FCNTL_H #include /* * FMODE_EXEC is 0x20 * These cannot be used by userspace O_* until internal and external open * flags are split. * -Eric Paris */ /* * When introducing new O_* bits, please check its uniqueness in fcntl_init(). */ #define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 #define O_RDWR 00000002 #ifndef O_CREAT #define O_CREAT 00000100 /* not fcntl */ #endif #ifndef O_EXCL #define O_EXCL 00000200 /* not fcntl */ #endif #ifndef O_NOCTTY #define O_NOCTTY 00000400 /* not fcntl */ #endif #ifndef O_TRUNC #define O_TRUNC 00001000 /* not fcntl */ #endif #ifndef O_APPEND #define O_APPEND 00002000 #endif #ifndef O_NONBLOCK #define O_NONBLOCK 00004000 #endif #ifndef O_DSYNC #define O_DSYNC 00010000 /* used to be O_SYNC, see below */ #endif #ifndef FASYNC #define FASYNC 00020000 /* fcntl, for BSD compatibility */ #endif #ifndef O_DIRECT #define O_DIRECT 00040000 /* direct disk access hint */ #endif #ifndef O_LARGEFILE #define O_LARGEFILE 00100000 #endif #ifndef O_DIRECTORY #define O_DIRECTORY 00200000 /* must be a directory */ #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 /* don't follow links */ #endif #ifndef O_NOATIME #define O_NOATIME 01000000 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 /* set close_on_exec */ #endif /* * Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using * the O_SYNC flag. We continue to use the existing numerical value * for O_DSYNC semantics now, but using the correct symbolic name for it. * This new value is used to request true Posix O_SYNC semantics. It is * defined in this strange way to make sure applications compiled against * new headers get at least O_DSYNC semantics on older kernels. * * This has the nice side-effect that we can simply test for O_DSYNC * wherever we do not care if O_DSYNC or O_SYNC is used. * * Note: __O_SYNC must never be used directly. */ #ifndef O_SYNC #define __O_SYNC 04000000 #define O_SYNC (__O_SYNC|O_DSYNC) #endif #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef __O_TMPFILE #define __O_TMPFILE 020000000 #endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ #define F_GETFL 3 /* get file->f_flags */ #define F_SETFL 4 /* set file->f_flags */ #ifndef F_GETLK #define F_GETLK 5 #define F_SETLK 6 #define F_SETLKW 7 #endif #ifndef F_SETOWN #define F_SETOWN 8 /* for sockets. */ #define F_GETOWN 9 /* for sockets. */ #endif #ifndef F_SETSIG #define F_SETSIG 10 /* for sockets. */ #define F_GETSIG 11 /* for sockets. */ #endif #if __BITS_PER_LONG == 32 || defined(__KERNEL__) #ifndef F_GETLK64 #define F_GETLK64 12 /* using 'struct flock64' */ #define F_SETLK64 13 #define F_SETLKW64 14 #endif #endif /* __BITS_PER_LONG == 32 || defined(__KERNEL__) */ #ifndef F_SETOWN_EX #define F_SETOWN_EX 15 #define F_GETOWN_EX 16 #endif #ifndef F_GETOWNER_UIDS #define F_GETOWNER_UIDS 17 #endif /* * Open File Description Locks * * Usually record locks held by a process are released on *any* close and are * not inherited across a fork(). * * These cmd values will set locks that conflict with process-associated * record locks, but are "owned" by the open file description, not the * process. This means that they are inherited across fork() like BSD (flock) * locks, and they are only released automatically when the last reference to * the the open file against which they were acquired is put. */ #define F_OFD_GETLK 36 #define F_OFD_SETLK 37 #define F_OFD_SETLKW 38 #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 struct f_owner_ex { int type; __kernel_pid_t pid; }; /* for F_[GET|SET]FL */ #define FD_CLOEXEC 1 /* actually anything with low bit set goes */ /* for posix fcntl() and lockf() */ #ifndef F_RDLCK #define F_RDLCK 0 #define F_WRLCK 1 #define F_UNLCK 2 #endif /* for old implementation of bsd flock () */ #ifndef F_EXLCK #define F_EXLCK 4 /* or 3 */ #define F_SHLCK 8 /* or 4 */ #endif /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ #define LOCK_NB 4 /* or'd with one of the above to prevent blocking */ #define LOCK_UN 8 /* remove lock */ /* * LOCK_MAND support has been removed from the kernel. We leave the symbols * here to not break legacy builds, but these should not be used in new code. */ #define LOCK_MAND 32 /* This is a mandatory flock ... */ #define LOCK_READ 64 /* which allows concurrent read operations */ #define LOCK_WRITE 128 /* which allows concurrent write operations */ #define LOCK_RW 192 /* which allows concurrent read & write ops */ #define F_LINUX_SPECIFIC_BASE 1024 #ifndef HAVE_ARCH_STRUCT_FLOCK struct flock { short l_type; short l_whence; __kernel_off_t l_start; __kernel_off_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK_EXTRA_SYSID __ARCH_FLOCK_EXTRA_SYSID #endif #ifdef __ARCH_FLOCK_PAD __ARCH_FLOCK_PAD #endif }; struct flock64 { short l_type; short l_whence; __kernel_loff_t l_start; __kernel_loff_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK64_PAD __ARCH_FLOCK64_PAD #endif }; #endif /* HAVE_ARCH_STRUCT_FLOCK */ #endif /* _ASM_GENERIC_FCNTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/ioctl.h000066400000000000000000000067471506776124200223000ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_IOCTL_H #define _UAPI_ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 /* * Let any architecture override either of the following before * including this file. */ #ifndef _IOC_SIZEBITS # define _IOC_SIZEBITS 14 #endif #ifndef _IOC_DIRBITS # define _IOC_DIRBITS 2 #endif #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits, which any architecture can choose to override * before including this file. * * NOTE: _IOC_WRITE means userland is writing and kernel is * reading. _IOC_READ means userland is reading and kernel is writing. */ #ifndef _IOC_NONE # define _IOC_NONE 0U #endif #ifndef _IOC_WRITE # define _IOC_WRITE 1U #endif #ifndef _IOC_READ # define _IOC_READ 2U #endif #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) #ifndef __KERNEL__ #define _IOC_TYPECHECK(t) (sizeof(t)) #endif /* * Used to create numbers. * * NOTE: _IOW means userland is writing and kernel is reading. _IOR * means userland is reading and kernel is writing. */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOR_BAD(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),sizeof(argtype)) #define _IOW_BAD(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),sizeof(argtype)) #define _IOWR_BAD(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(argtype)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _UAPI_ASM_GENERIC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/posix_types.h000066400000000000000000000045421506776124200235430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_GENERIC_POSIX_TYPES_H #define __ASM_GENERIC_POSIX_TYPES_H #include /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. * * First the types that are often defined in different ways across * architectures, so that you can override them. */ #ifndef __kernel_long_t typedef long __kernel_long_t; typedef unsigned long __kernel_ulong_t; #endif #ifndef __kernel_ino_t typedef __kernel_ulong_t __kernel_ino_t; #endif #ifndef __kernel_mode_t typedef unsigned int __kernel_mode_t; #endif #ifndef __kernel_pid_t typedef int __kernel_pid_t; #endif #ifndef __kernel_ipc_pid_t typedef int __kernel_ipc_pid_t; #endif #ifndef __kernel_uid_t typedef unsigned int __kernel_uid_t; typedef unsigned int __kernel_gid_t; #endif #ifndef __kernel_suseconds_t typedef __kernel_long_t __kernel_suseconds_t; #endif #ifndef __kernel_daddr_t typedef int __kernel_daddr_t; #endif #ifndef __kernel_uid32_t typedef unsigned int __kernel_uid32_t; typedef unsigned int __kernel_gid32_t; #endif #ifndef __kernel_old_uid_t typedef __kernel_uid_t __kernel_old_uid_t; typedef __kernel_gid_t __kernel_old_gid_t; #endif #ifndef __kernel_old_dev_t typedef unsigned int __kernel_old_dev_t; #endif /* * Most 32 bit architectures use "unsigned int" size_t, * and all 64 bit architectures use "unsigned long" size_t. */ #ifndef __kernel_size_t #if __BITS_PER_LONG != 64 typedef unsigned int __kernel_size_t; typedef int __kernel_ssize_t; typedef int __kernel_ptrdiff_t; #else typedef __kernel_ulong_t __kernel_size_t; typedef __kernel_long_t __kernel_ssize_t; typedef __kernel_long_t __kernel_ptrdiff_t; #endif #endif #ifndef __kernel_fsid_t typedef struct { int val[2]; } __kernel_fsid_t; #endif /* * anything below here should be completely generic */ typedef __kernel_long_t __kernel_off_t; typedef long long __kernel_loff_t; typedef __kernel_long_t __kernel_old_time_t; #ifndef __KERNEL__ typedef __kernel_long_t __kernel_time_t; #endif typedef long long __kernel_time64_t; typedef __kernel_long_t __kernel_clock_t; typedef int __kernel_timer_t; typedef int __kernel_clockid_t; typedef char * __kernel_caddr_t; typedef unsigned short __kernel_uid16_t; typedef unsigned short __kernel_gid16_t; #endif /* __ASM_GENERIC_POSIX_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/rwonce.h000066400000000000000000000062051506776124200224500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/swab.h000066400000000000000000000007661506776124200221150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_SWAB_H #define _ASM_GENERIC_SWAB_H #include /* * 32 bit architectures typically (but not always) want to * set __SWAB_64_THRU_32__. In user space, this is only * valid if the compiler supports 64 bit data types. */ #if __BITS_PER_LONG == 32 #if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__) #define __SWAB_64_THRU_32__ #endif #endif #endif /* _ASM_GENERIC_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/riscv/types.h000066400000000000000000000003701506776124200223140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_TYPES_H #define _UAPI_ASM_GENERIC_TYPES_H /* * int-ll64 is used everywhere now. */ #include #endif /* _UAPI_ASM_GENERIC_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/s390/000077500000000000000000000000001506776124200203475ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/s390/bitsperlong.h000066400000000000000000000004451506776124200230530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_S390_BITSPERLONG_H #define __ASM_S390_BITSPERLONG_H #ifndef __s390x__ #define __BITS_PER_LONG 32 #else #define __BITS_PER_LONG 64 #endif #include #endif /* __ASM_S390_BITSPERLONG_H */ bpftrace-0.24.1/src/stdlib/include/asm/s390/byteorder.h000066400000000000000000000002741506776124200225220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _S390_BYTEORDER_H #define _S390_BYTEORDER_H #include #endif /* _S390_BYTEORDER_H */ bpftrace-0.24.1/src/stdlib/include/asm/s390/errno.h000066400000000000000000000130201506776124200216410ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return * -ENOSYS if users try to call a syscall that doesn't exist. To keep * failures of syscalls that really do exist distinguishable from * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ #define ENOSYS 38 /* Invalid system call number */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif bpftrace-0.24.1/src/stdlib/include/asm/s390/fcntl.h000066400000000000000000000126731506776124200216370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_FCNTL_H #define _ASM_GENERIC_FCNTL_H #include /* * FMODE_EXEC is 0x20 * These cannot be used by userspace O_* until internal and external open * flags are split. * -Eric Paris */ /* * When introducing new O_* bits, please check its uniqueness in fcntl_init(). */ #define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 #define O_RDWR 00000002 #ifndef O_CREAT #define O_CREAT 00000100 /* not fcntl */ #endif #ifndef O_EXCL #define O_EXCL 00000200 /* not fcntl */ #endif #ifndef O_NOCTTY #define O_NOCTTY 00000400 /* not fcntl */ #endif #ifndef O_TRUNC #define O_TRUNC 00001000 /* not fcntl */ #endif #ifndef O_APPEND #define O_APPEND 00002000 #endif #ifndef O_NONBLOCK #define O_NONBLOCK 00004000 #endif #ifndef O_DSYNC #define O_DSYNC 00010000 /* used to be O_SYNC, see below */ #endif #ifndef FASYNC #define FASYNC 00020000 /* fcntl, for BSD compatibility */ #endif #ifndef O_DIRECT #define O_DIRECT 00040000 /* direct disk access hint */ #endif #ifndef O_LARGEFILE #define O_LARGEFILE 00100000 #endif #ifndef O_DIRECTORY #define O_DIRECTORY 00200000 /* must be a directory */ #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 /* don't follow links */ #endif #ifndef O_NOATIME #define O_NOATIME 01000000 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 /* set close_on_exec */ #endif /* * Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using * the O_SYNC flag. We continue to use the existing numerical value * for O_DSYNC semantics now, but using the correct symbolic name for it. * This new value is used to request true Posix O_SYNC semantics. It is * defined in this strange way to make sure applications compiled against * new headers get at least O_DSYNC semantics on older kernels. * * This has the nice side-effect that we can simply test for O_DSYNC * wherever we do not care if O_DSYNC or O_SYNC is used. * * Note: __O_SYNC must never be used directly. */ #ifndef O_SYNC #define __O_SYNC 04000000 #define O_SYNC (__O_SYNC|O_DSYNC) #endif #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef __O_TMPFILE #define __O_TMPFILE 020000000 #endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ #define F_GETFL 3 /* get file->f_flags */ #define F_SETFL 4 /* set file->f_flags */ #ifndef F_GETLK #define F_GETLK 5 #define F_SETLK 6 #define F_SETLKW 7 #endif #ifndef F_SETOWN #define F_SETOWN 8 /* for sockets. */ #define F_GETOWN 9 /* for sockets. */ #endif #ifndef F_SETSIG #define F_SETSIG 10 /* for sockets. */ #define F_GETSIG 11 /* for sockets. */ #endif #if __BITS_PER_LONG == 32 || defined(__KERNEL__) #ifndef F_GETLK64 #define F_GETLK64 12 /* using 'struct flock64' */ #define F_SETLK64 13 #define F_SETLKW64 14 #endif #endif /* __BITS_PER_LONG == 32 || defined(__KERNEL__) */ #ifndef F_SETOWN_EX #define F_SETOWN_EX 15 #define F_GETOWN_EX 16 #endif #ifndef F_GETOWNER_UIDS #define F_GETOWNER_UIDS 17 #endif /* * Open File Description Locks * * Usually record locks held by a process are released on *any* close and are * not inherited across a fork(). * * These cmd values will set locks that conflict with process-associated * record locks, but are "owned" by the open file description, not the * process. This means that they are inherited across fork() like BSD (flock) * locks, and they are only released automatically when the last reference to * the the open file against which they were acquired is put. */ #define F_OFD_GETLK 36 #define F_OFD_SETLK 37 #define F_OFD_SETLKW 38 #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 struct f_owner_ex { int type; __kernel_pid_t pid; }; /* for F_[GET|SET]FL */ #define FD_CLOEXEC 1 /* actually anything with low bit set goes */ /* for posix fcntl() and lockf() */ #ifndef F_RDLCK #define F_RDLCK 0 #define F_WRLCK 1 #define F_UNLCK 2 #endif /* for old implementation of bsd flock () */ #ifndef F_EXLCK #define F_EXLCK 4 /* or 3 */ #define F_SHLCK 8 /* or 4 */ #endif /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ #define LOCK_NB 4 /* or'd with one of the above to prevent blocking */ #define LOCK_UN 8 /* remove lock */ /* * LOCK_MAND support has been removed from the kernel. We leave the symbols * here to not break legacy builds, but these should not be used in new code. */ #define LOCK_MAND 32 /* This is a mandatory flock ... */ #define LOCK_READ 64 /* which allows concurrent read operations */ #define LOCK_WRITE 128 /* which allows concurrent write operations */ #define LOCK_RW 192 /* which allows concurrent read & write ops */ #define F_LINUX_SPECIFIC_BASE 1024 #ifndef HAVE_ARCH_STRUCT_FLOCK struct flock { short l_type; short l_whence; __kernel_off_t l_start; __kernel_off_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK_EXTRA_SYSID __ARCH_FLOCK_EXTRA_SYSID #endif #ifdef __ARCH_FLOCK_PAD __ARCH_FLOCK_PAD #endif }; struct flock64 { short l_type; short l_whence; __kernel_loff_t l_start; __kernel_loff_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK64_PAD __ARCH_FLOCK64_PAD #endif }; #endif /* HAVE_ARCH_STRUCT_FLOCK */ #endif /* _ASM_GENERIC_FCNTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/s390/ioctl.h000066400000000000000000000067471506776124200216500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_IOCTL_H #define _UAPI_ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 /* * Let any architecture override either of the following before * including this file. */ #ifndef _IOC_SIZEBITS # define _IOC_SIZEBITS 14 #endif #ifndef _IOC_DIRBITS # define _IOC_DIRBITS 2 #endif #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits, which any architecture can choose to override * before including this file. * * NOTE: _IOC_WRITE means userland is writing and kernel is * reading. _IOC_READ means userland is reading and kernel is writing. */ #ifndef _IOC_NONE # define _IOC_NONE 0U #endif #ifndef _IOC_WRITE # define _IOC_WRITE 1U #endif #ifndef _IOC_READ # define _IOC_READ 2U #endif #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) #ifndef __KERNEL__ #define _IOC_TYPECHECK(t) (sizeof(t)) #endif /* * Used to create numbers. * * NOTE: _IOW means userland is writing and kernel is reading. _IOR * means userland is reading and kernel is writing. */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOR_BAD(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),sizeof(argtype)) #define _IOW_BAD(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),sizeof(argtype)) #define _IOWR_BAD(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(argtype)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _UAPI_ASM_GENERIC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/s390/posix_types.h000066400000000000000000000030721506776124200231100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * S390 version * */ #ifndef __ARCH_S390_POSIX_TYPES_H #define __ARCH_S390_POSIX_TYPES_H /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. Also, we cannot * assume GCC is being used. */ typedef unsigned long __kernel_size_t; typedef long __kernel_ssize_t; #define __kernel_size_t __kernel_size_t typedef unsigned short __kernel_old_dev_t; #define __kernel_old_dev_t __kernel_old_dev_t #ifdef __KERNEL__ typedef unsigned short __kernel_old_uid_t; typedef unsigned short __kernel_old_gid_t; #define __kernel_old_uid_t __kernel_old_uid_t #endif #ifndef __s390x__ typedef unsigned long __kernel_ino_t; typedef unsigned short __kernel_mode_t; typedef unsigned short __kernel_ipc_pid_t; typedef unsigned short __kernel_uid_t; typedef unsigned short __kernel_gid_t; typedef int __kernel_ptrdiff_t; #else /* __s390x__ */ typedef unsigned int __kernel_ino_t; typedef unsigned int __kernel_mode_t; typedef int __kernel_ipc_pid_t; typedef unsigned int __kernel_uid_t; typedef unsigned int __kernel_gid_t; typedef long __kernel_ptrdiff_t; typedef unsigned long __kernel_sigset_t; /* at least 32 bits */ #endif /* __s390x__ */ #define __kernel_ino_t __kernel_ino_t #define __kernel_mode_t __kernel_mode_t #define __kernel_ipc_pid_t __kernel_ipc_pid_t #define __kernel_uid_t __kernel_uid_t #define __kernel_gid_t __kernel_gid_t #include #endif bpftrace-0.24.1/src/stdlib/include/asm/s390/rwonce.h000066400000000000000000000062051506776124200220200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/s390/swab.h000066400000000000000000000007661506776124200214650ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_SWAB_H #define _ASM_GENERIC_SWAB_H #include /* * 32 bit architectures typically (but not always) want to * set __SWAB_64_THRU_32__. In user space, this is only * valid if the compiler supports 64 bit data types. */ #if __BITS_PER_LONG == 32 #if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__) #define __SWAB_64_THRU_32__ #endif #endif #endif /* _ASM_GENERIC_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/s390/types.h000066400000000000000000000007771506776124200216770ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * S390 version * * Derived from "include/asm-i386/types.h" */ #ifndef _UAPI_S390_TYPES_H #define _UAPI_S390_TYPES_H #include #ifndef __ASSEMBLY__ typedef unsigned long addr_t; typedef __signed__ long saddr_t; typedef struct { union { struct { __u64 high; __u64 low; }; __u32 u[4]; }; } __attribute__((packed, aligned(4))) __vector128; #endif /* __ASSEMBLY__ */ #endif /* _UAPI_S390_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/000077500000000000000000000000001506776124200202765ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/asm/x86/bitsperlong.h000066400000000000000000000005011506776124200227730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_X86_BITSPERLONG_H #define __ASM_X86_BITSPERLONG_H #if defined(__x86_64__) && !defined(__ILP32__) # define __BITS_PER_LONG 64 #else # define __BITS_PER_LONG 32 #endif #include #endif /* __ASM_X86_BITSPERLONG_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/byteorder.h000066400000000000000000000003101506776124200224400ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_X86_BYTEORDER_H #define _ASM_X86_BYTEORDER_H #include #endif /* _ASM_X86_BYTEORDER_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/errno.h000066400000000000000000000130201506776124200215700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return * -ENOSYS if users try to call a syscall that doesn't exist. To keep * failures of syscalls that really do exist distinguishable from * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ #define ENOSYS 38 /* Invalid system call number */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif bpftrace-0.24.1/src/stdlib/include/asm/x86/fcntl.h000066400000000000000000000126731506776124200215660ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_GENERIC_FCNTL_H #define _ASM_GENERIC_FCNTL_H #include /* * FMODE_EXEC is 0x20 * These cannot be used by userspace O_* until internal and external open * flags are split. * -Eric Paris */ /* * When introducing new O_* bits, please check its uniqueness in fcntl_init(). */ #define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 #define O_RDWR 00000002 #ifndef O_CREAT #define O_CREAT 00000100 /* not fcntl */ #endif #ifndef O_EXCL #define O_EXCL 00000200 /* not fcntl */ #endif #ifndef O_NOCTTY #define O_NOCTTY 00000400 /* not fcntl */ #endif #ifndef O_TRUNC #define O_TRUNC 00001000 /* not fcntl */ #endif #ifndef O_APPEND #define O_APPEND 00002000 #endif #ifndef O_NONBLOCK #define O_NONBLOCK 00004000 #endif #ifndef O_DSYNC #define O_DSYNC 00010000 /* used to be O_SYNC, see below */ #endif #ifndef FASYNC #define FASYNC 00020000 /* fcntl, for BSD compatibility */ #endif #ifndef O_DIRECT #define O_DIRECT 00040000 /* direct disk access hint */ #endif #ifndef O_LARGEFILE #define O_LARGEFILE 00100000 #endif #ifndef O_DIRECTORY #define O_DIRECTORY 00200000 /* must be a directory */ #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 00400000 /* don't follow links */ #endif #ifndef O_NOATIME #define O_NOATIME 01000000 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 /* set close_on_exec */ #endif /* * Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using * the O_SYNC flag. We continue to use the existing numerical value * for O_DSYNC semantics now, but using the correct symbolic name for it. * This new value is used to request true Posix O_SYNC semantics. It is * defined in this strange way to make sure applications compiled against * new headers get at least O_DSYNC semantics on older kernels. * * This has the nice side-effect that we can simply test for O_DSYNC * wherever we do not care if O_DSYNC or O_SYNC is used. * * Note: __O_SYNC must never be used directly. */ #ifndef O_SYNC #define __O_SYNC 04000000 #define O_SYNC (__O_SYNC|O_DSYNC) #endif #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef __O_TMPFILE #define __O_TMPFILE 020000000 #endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ #define F_GETFL 3 /* get file->f_flags */ #define F_SETFL 4 /* set file->f_flags */ #ifndef F_GETLK #define F_GETLK 5 #define F_SETLK 6 #define F_SETLKW 7 #endif #ifndef F_SETOWN #define F_SETOWN 8 /* for sockets. */ #define F_GETOWN 9 /* for sockets. */ #endif #ifndef F_SETSIG #define F_SETSIG 10 /* for sockets. */ #define F_GETSIG 11 /* for sockets. */ #endif #if __BITS_PER_LONG == 32 || defined(__KERNEL__) #ifndef F_GETLK64 #define F_GETLK64 12 /* using 'struct flock64' */ #define F_SETLK64 13 #define F_SETLKW64 14 #endif #endif /* __BITS_PER_LONG == 32 || defined(__KERNEL__) */ #ifndef F_SETOWN_EX #define F_SETOWN_EX 15 #define F_GETOWN_EX 16 #endif #ifndef F_GETOWNER_UIDS #define F_GETOWNER_UIDS 17 #endif /* * Open File Description Locks * * Usually record locks held by a process are released on *any* close and are * not inherited across a fork(). * * These cmd values will set locks that conflict with process-associated * record locks, but are "owned" by the open file description, not the * process. This means that they are inherited across fork() like BSD (flock) * locks, and they are only released automatically when the last reference to * the the open file against which they were acquired is put. */ #define F_OFD_GETLK 36 #define F_OFD_SETLK 37 #define F_OFD_SETLKW 38 #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 struct f_owner_ex { int type; __kernel_pid_t pid; }; /* for F_[GET|SET]FL */ #define FD_CLOEXEC 1 /* actually anything with low bit set goes */ /* for posix fcntl() and lockf() */ #ifndef F_RDLCK #define F_RDLCK 0 #define F_WRLCK 1 #define F_UNLCK 2 #endif /* for old implementation of bsd flock () */ #ifndef F_EXLCK #define F_EXLCK 4 /* or 3 */ #define F_SHLCK 8 /* or 4 */ #endif /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ #define LOCK_NB 4 /* or'd with one of the above to prevent blocking */ #define LOCK_UN 8 /* remove lock */ /* * LOCK_MAND support has been removed from the kernel. We leave the symbols * here to not break legacy builds, but these should not be used in new code. */ #define LOCK_MAND 32 /* This is a mandatory flock ... */ #define LOCK_READ 64 /* which allows concurrent read operations */ #define LOCK_WRITE 128 /* which allows concurrent write operations */ #define LOCK_RW 192 /* which allows concurrent read & write ops */ #define F_LINUX_SPECIFIC_BASE 1024 #ifndef HAVE_ARCH_STRUCT_FLOCK struct flock { short l_type; short l_whence; __kernel_off_t l_start; __kernel_off_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK_EXTRA_SYSID __ARCH_FLOCK_EXTRA_SYSID #endif #ifdef __ARCH_FLOCK_PAD __ARCH_FLOCK_PAD #endif }; struct flock64 { short l_type; short l_whence; __kernel_loff_t l_start; __kernel_loff_t l_len; __kernel_pid_t l_pid; #ifdef __ARCH_FLOCK64_PAD __ARCH_FLOCK64_PAD #endif }; #endif /* HAVE_ARCH_STRUCT_FLOCK */ #endif /* _ASM_GENERIC_FCNTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/ioctl.h000066400000000000000000000067471506776124200215770ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_IOCTL_H #define _UAPI_ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 /* * Let any architecture override either of the following before * including this file. */ #ifndef _IOC_SIZEBITS # define _IOC_SIZEBITS 14 #endif #ifndef _IOC_DIRBITS # define _IOC_DIRBITS 2 #endif #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits, which any architecture can choose to override * before including this file. * * NOTE: _IOC_WRITE means userland is writing and kernel is * reading. _IOC_READ means userland is reading and kernel is writing. */ #ifndef _IOC_NONE # define _IOC_NONE 0U #endif #ifndef _IOC_WRITE # define _IOC_WRITE 1U #endif #ifndef _IOC_READ # define _IOC_READ 2U #endif #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) #ifndef __KERNEL__ #define _IOC_TYPECHECK(t) (sizeof(t)) #endif /* * Used to create numbers. * * NOTE: _IOW means userland is writing and kernel is reading. _IOR * means userland is reading and kernel is writing. */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype))) #define _IOR_BAD(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),sizeof(argtype)) #define _IOW_BAD(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),sizeof(argtype)) #define _IOWR_BAD(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(argtype)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _UAPI_ASM_GENERIC_IOCTL_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/posix_types.h000066400000000000000000000003721506776124200230370ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __KERNEL__ # ifdef __i386__ # include # elif defined(__ILP32__) # include # else # include # endif #endif bpftrace-0.24.1/src/stdlib/include/asm/x86/posix_types_64.h000066400000000000000000000011411506776124200233430ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_X86_POSIX_TYPES_64_H #define _ASM_X86_POSIX_TYPES_64_H /* * This file is generally used by user-level software, so you need to * be a little careful about namespace pollution etc. Also, we cannot * assume GCC is being used. */ typedef unsigned short __kernel_old_uid_t; typedef unsigned short __kernel_old_gid_t; #define __kernel_old_uid_t __kernel_old_uid_t typedef unsigned long __kernel_old_dev_t; #define __kernel_old_dev_t __kernel_old_dev_t #include #endif /* _ASM_X86_POSIX_TYPES_64_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/rwonce.h000066400000000000000000000062051506776124200217470ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */ #ifndef __ASM_GENERIC_RWONCE_H #define __ASM_GENERIC_RWONCE_H #ifndef __ASSEMBLY__ #include #include #include /* * Yes, this permits 64-bit accesses on 32-bit architectures. These will * actually be atomic in some cases (namely Armv7 + LPAE), but for others we * rely on the access being split into 2x32-bit accesses for a 32-bit quantity * (e.g. a virtual address) and a strong prevailing wind. */ #define compiletime_assert_rwonce_type(t) \ compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ "Unsupported access size for {READ,WRITE}_ONCE().") /* * Use __READ_ONCE() instead of READ_ONCE() if you do not require any * atomicity. Note that this may result in tears! */ #ifndef __READ_ONCE #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) #endif #define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE(x); \ }) #define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) #define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE(x, val); \ } while (0) static __no_sanitize_or_inline unsigned long __read_once_word_nocheck(const void *addr) { return __READ_ONCE(*(unsigned long *)addr); } /* * Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a * word from memory atomically but without telling KASAN/KCSAN. This is * usually used by unwinding code when walking the stack of a running process. */ #define READ_ONCE_NOCHECK(x) \ ({ \ compiletime_assert(sizeof(x) == sizeof(unsigned long), \ "Unsupported access size for READ_ONCE_NOCHECK()."); \ (typeof(x))__read_once_word_nocheck(&(x)); \ }) static __no_sanitize_or_inline unsigned long read_word_at_a_time(const void *addr) { /* open-coded instrument_read(addr, 1) */ kasan_check_read(addr, 1); kcsan_check_read(addr, 1); /* * This load can race with concurrent stores to out-of-bounds memory, * but READ_ONCE() can't be used because it requires higher alignment * than plain loads in arm64 builds with LTO. */ return *(unsigned long *)addr; } #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_RWONCE_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/swab.h000066400000000000000000000014011506776124200213770ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _ASM_X86_SWAB_H #define _ASM_X86_SWAB_H #include #include static inline __attribute_const__ __u32 __arch_swab32(__u32 val) { asm("bswapl %0" : "=r" (val) : "0" (val)); return val; } #define __arch_swab32 __arch_swab32 static inline __attribute_const__ __u64 __arch_swab64(__u64 val) { #ifdef __i386__ union { struct { __u32 a; __u32 b; } s; __u64 u; } v; v.u = val; asm("bswapl %0 ; bswapl %1 ; xchgl %0,%1" : "=r" (v.s.a), "=r" (v.s.b) : "0" (v.s.a), "1" (v.s.b)); return v.u; #else /* __i386__ */ asm("bswapq %0" : "=r" (val) : "0" (val)); return val; #endif } #define __arch_swab64 __arch_swab64 #endif /* _ASM_X86_SWAB_H */ bpftrace-0.24.1/src/stdlib/include/asm/x86/types.h000066400000000000000000000003701506776124200216130ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_ASM_GENERIC_TYPES_H #define _UAPI_ASM_GENERIC_TYPES_H /* * int-ll64 is used everywhere now. */ #include #endif /* _UAPI_ASM_GENERIC_TYPES_H */ bpftrace-0.24.1/src/stdlib/include/float.h000066400000000000000000000121071506776124200203500ustar00rootroot00000000000000/*===---- 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.24.1/src/stdlib/include/limits.h000066400000000000000000000072261506776124200205520ustar00rootroot00000000000000/*===---- 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.24.1/src/stdlib/include/linux/000077500000000000000000000000001506776124200202305ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/linux/bpf.h000066400000000000000000010471101506776124200211540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ #ifndef _UAPI__LINUX_BPF_H__ #define _UAPI__LINUX_BPF_H__ #include #include /* Extended instruction set based on top of classic BPF */ /* instruction classes */ #define BPF_JMP32 0x06 /* jmp mode in word width */ #define BPF_ALU64 0x07 /* alu mode in double word width */ /* ld/ldx fields */ #define BPF_DW 0x18 /* double word (64-bit) */ #define BPF_MEMSX 0x80 /* load with sign extension */ #define BPF_ATOMIC 0xc0 /* atomic memory ops - op type in immediate */ #define BPF_XADD 0xc0 /* exclusive add - legacy name */ /* alu/jmp fields */ #define BPF_MOV 0xb0 /* mov reg to reg */ #define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */ /* change endianness of a register */ #define BPF_END 0xd0 /* flags for endianness conversion: */ #define BPF_TO_LE 0x00 /* convert to little-endian */ #define BPF_TO_BE 0x08 /* convert to big-endian */ #define BPF_FROM_LE BPF_TO_LE #define BPF_FROM_BE BPF_TO_BE /* jmp encodings */ #define BPF_JNE 0x50 /* jump != */ #define BPF_JLT 0xa0 /* LT is unsigned, '<' */ #define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ #define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ #define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ #define BPF_JSLT 0xc0 /* SLT is signed, '<' */ #define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ #define BPF_JCOND 0xe0 /* conditional pseudo jumps: may_goto, goto_or_nop */ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ /* atomic op type fields (stored in immediate) */ #define BPF_FETCH 0x01 /* not an opcode on its own, used to build others */ #define BPF_XCHG (0xe0 | BPF_FETCH) /* atomic exchange */ #define BPF_CMPXCHG (0xf0 | BPF_FETCH) /* atomic compare-and-write */ #define BPF_LOAD_ACQ 0x100 /* load-acquire */ #define BPF_STORE_REL 0x110 /* store-release */ enum bpf_cond_pseudo_jmp { BPF_MAY_GOTO = 0, }; /* Register numbers */ enum { BPF_REG_0 = 0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5, BPF_REG_6, BPF_REG_7, BPF_REG_8, BPF_REG_9, BPF_REG_10, __MAX_BPF_REG, }; /* BPF has 10 general purpose 64-bit registers and stack frame. */ #define MAX_BPF_REG __MAX_BPF_REG struct bpf_insn { __u8 code; /* opcode */ __u8 dst_reg:4; /* dest register */ __u8 src_reg:4; /* source register */ __s16 off; /* signed offset */ __s32 imm; /* signed immediate constant */ }; /* Deprecated: use struct bpf_lpm_trie_key_u8 (when the "data" member is needed for * byte access) or struct bpf_lpm_trie_key_hdr (when using an alternative type for * the trailing flexible array member) instead. */ struct bpf_lpm_trie_key { __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ __u8 data[0]; /* Arbitrary size */ }; /* Header for bpf_lpm_trie_key structs */ struct bpf_lpm_trie_key_hdr { __u32 prefixlen; }; /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry, with trailing byte array. */ struct bpf_lpm_trie_key_u8 { union { struct bpf_lpm_trie_key_hdr hdr; __u32 prefixlen; }; __u8 data[]; /* Arbitrary size */ }; struct bpf_cgroup_storage_key { __u64 cgroup_inode_id; /* cgroup inode id */ __u32 attach_type; /* program attach type (enum bpf_attach_type) */ }; enum bpf_cgroup_iter_order { BPF_CGROUP_ITER_ORDER_UNSPEC = 0, BPF_CGROUP_ITER_SELF_ONLY, /* process only a single object. */ BPF_CGROUP_ITER_DESCENDANTS_PRE, /* walk descendants in pre-order. */ BPF_CGROUP_ITER_DESCENDANTS_POST, /* walk descendants in post-order. */ BPF_CGROUP_ITER_ANCESTORS_UP, /* walk ancestors upward. */ }; union bpf_iter_link_info { struct { __u32 map_fd; } map; struct { enum bpf_cgroup_iter_order order; /* At most one of cgroup_fd and cgroup_id can be non-zero. If * both are zero, the walk starts from the default cgroup v2 * root. For walking v1 hierarchy, one should always explicitly * specify cgroup_fd. */ __u32 cgroup_fd; __u64 cgroup_id; } cgroup; /* Parameters of task iterators. */ struct { __u32 tid; __u32 pid; __u32 pid_fd; } task; }; /* BPF syscall commands, see bpf(2) man-page for more details. */ /** * DOC: eBPF Syscall Preamble * * The operation to be performed by the **bpf**\ () system call is determined * by the *cmd* argument. Each operation takes an accompanying argument, * provided via *attr*, which is a pointer to a union of type *bpf_attr* (see * below). The size argument is the size of the union pointed to by *attr*. */ /** * DOC: eBPF Syscall Commands * * BPF_MAP_CREATE * Description * Create a map and return a file descriptor that refers to the * map. The close-on-exec file descriptor flag (see **fcntl**\ (2)) * is automatically enabled for the new file descriptor. * * Applying **close**\ (2) to the file descriptor returned by * **BPF_MAP_CREATE** will delete the map (but see NOTES). * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_MAP_LOOKUP_ELEM * Description * Look up an element with a given *key* in the map referred to * by the file descriptor *map_fd*. * * The *flags* argument may be specified as one of the * following: * * **BPF_F_LOCK** * Look up the value of a spin-locked map without * returning the lock. This must be specified if the * elements contain a spinlock. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_MAP_UPDATE_ELEM * Description * Create or update an element (key/value pair) in a specified map. * * The *flags* argument should be specified as one of the * following: * * **BPF_ANY** * Create a new element or update an existing element. * **BPF_NOEXIST** * Create a new element only if it did not exist. * **BPF_EXIST** * Update an existing element. * **BPF_F_LOCK** * Update a spin_lock-ed map element. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**, * **E2BIG**, **EEXIST**, or **ENOENT**. * * **E2BIG** * The number of elements in the map reached the * *max_entries* limit specified at map creation time. * **EEXIST** * If *flags* specifies **BPF_NOEXIST** and the element * with *key* already exists in the map. * **ENOENT** * If *flags* specifies **BPF_EXIST** and the element with * *key* does not exist in the map. * * BPF_MAP_DELETE_ELEM * Description * Look up and delete an element by key in a specified map. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_MAP_GET_NEXT_KEY * Description * Look up an element by key in a specified map and return the key * of the next element. Can be used to iterate over all elements * in the map. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * The following cases can be used to iterate over all elements of * the map: * * * If *key* is not found, the operation returns zero and sets * the *next_key* pointer to the key of the first element. * * If *key* is found, the operation returns zero and sets the * *next_key* pointer to the key of the next element. * * If *key* is the last element, returns -1 and *errno* is set * to **ENOENT**. * * May set *errno* to **ENOMEM**, **EFAULT**, **EPERM**, or * **EINVAL** on error. * * BPF_PROG_LOAD * Description * Verify and load an eBPF program, returning a new file * descriptor associated with the program. * * Applying **close**\ (2) to the file descriptor returned by * **BPF_PROG_LOAD** will unload the eBPF program (but see NOTES). * * The close-on-exec file descriptor flag (see **fcntl**\ (2)) is * automatically enabled for the new file descriptor. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_OBJ_PIN * Description * Pin an eBPF program or map referred by the specified *bpf_fd* * to the provided *pathname* on the filesystem. * * The *pathname* argument must not contain a dot ("."). * * On success, *pathname* retains a reference to the eBPF object, * preventing deallocation of the object when the original * *bpf_fd* is closed. This allow the eBPF object to live beyond * **close**\ (\ *bpf_fd*\ ), and hence the lifetime of the parent * process. * * Applying **unlink**\ (2) or similar calls to the *pathname* * unpins the object from the filesystem, removing the reference. * If no other file descriptors or filesystem nodes refer to the * same object, it will be deallocated (see NOTES). * * The filesystem type for the parent directory of *pathname* must * be **BPF_FS_MAGIC**. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_OBJ_GET * Description * Open a file descriptor for the eBPF object pinned to the * specified *pathname*. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_PROG_ATTACH * Description * Attach an eBPF program to a *target_fd* at the specified * *attach_type* hook. * * The *attach_type* specifies the eBPF attachment point to * attach the program to, and must be one of *bpf_attach_type* * (see below). * * The *attach_bpf_fd* must be a valid file descriptor for a * loaded eBPF program of a cgroup, flow dissector, LIRC, sockmap * or sock_ops type corresponding to the specified *attach_type*. * * The *target_fd* must be a valid file descriptor for a kernel * object which depends on the attach type of *attach_bpf_fd*: * * **BPF_PROG_TYPE_CGROUP_DEVICE**, * **BPF_PROG_TYPE_CGROUP_SKB**, * **BPF_PROG_TYPE_CGROUP_SOCK**, * **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**, * **BPF_PROG_TYPE_CGROUP_SOCKOPT**, * **BPF_PROG_TYPE_CGROUP_SYSCTL**, * **BPF_PROG_TYPE_SOCK_OPS** * * Control Group v2 hierarchy with the eBPF controller * enabled. Requires the kernel to be compiled with * **CONFIG_CGROUP_BPF**. * * **BPF_PROG_TYPE_FLOW_DISSECTOR** * * Network namespace (eg /proc/self/ns/net). * * **BPF_PROG_TYPE_LIRC_MODE2** * * LIRC device path (eg /dev/lircN). Requires the kernel * to be compiled with **CONFIG_BPF_LIRC_MODE2**. * * **BPF_PROG_TYPE_SK_SKB**, * **BPF_PROG_TYPE_SK_MSG** * * eBPF map of socket type (eg **BPF_MAP_TYPE_SOCKHASH**). * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_PROG_DETACH * Description * Detach the eBPF program associated with the *target_fd* at the * hook specified by *attach_type*. The program must have been * previously attached using **BPF_PROG_ATTACH**. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_PROG_TEST_RUN * Description * Run the eBPF program associated with the *prog_fd* a *repeat* * number of times against a provided program context *ctx_in* and * data *data_in*, and return the modified program context * *ctx_out*, *data_out* (for example, packet data), result of the * execution *retval*, and *duration* of the test run. * * The sizes of the buffers provided as input and output * parameters *ctx_in*, *ctx_out*, *data_in*, and *data_out* must * be provided in the corresponding variables *ctx_size_in*, * *ctx_size_out*, *data_size_in*, and/or *data_size_out*. If any * of these parameters are not provided (ie set to NULL), the * corresponding size field must be zero. * * Some program types have particular requirements: * * **BPF_PROG_TYPE_SK_LOOKUP** * *data_in* and *data_out* must be NULL. * * **BPF_PROG_TYPE_RAW_TRACEPOINT**, * **BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE** * * *ctx_out*, *data_in* and *data_out* must be NULL. * *repeat* must be zero. * * BPF_PROG_RUN is an alias for BPF_PROG_TEST_RUN. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * **ENOSPC** * Either *data_size_out* or *ctx_size_out* is too small. * **ENOTSUPP** * This command is not supported by the program type of * the program referred to by *prog_fd*. * * BPF_PROG_GET_NEXT_ID * Description * Fetch the next eBPF program currently loaded into the kernel. * * Looks for the eBPF program with an id greater than *start_id* * and updates *next_id* on success. If no other eBPF programs * remain with ids higher than *start_id*, returns -1 and sets * *errno* to **ENOENT**. * * Return * Returns zero on success. On error, or when no id remains, -1 * is returned and *errno* is set appropriately. * * BPF_MAP_GET_NEXT_ID * Description * Fetch the next eBPF map currently loaded into the kernel. * * Looks for the eBPF map with an id greater than *start_id* * and updates *next_id* on success. If no other eBPF maps * remain with ids higher than *start_id*, returns -1 and sets * *errno* to **ENOENT**. * * Return * Returns zero on success. On error, or when no id remains, -1 * is returned and *errno* is set appropriately. * * BPF_PROG_GET_FD_BY_ID * Description * Open a file descriptor for the eBPF program corresponding to * *prog_id*. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_MAP_GET_FD_BY_ID * Description * Open a file descriptor for the eBPF map corresponding to * *map_id*. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_OBJ_GET_INFO_BY_FD * Description * Obtain information about the eBPF object corresponding to * *bpf_fd*. * * Populates up to *info_len* bytes of *info*, which will be in * one of the following formats depending on the eBPF object type * of *bpf_fd*: * * * **struct bpf_prog_info** * * **struct bpf_map_info** * * **struct bpf_btf_info** * * **struct bpf_link_info** * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_PROG_QUERY * Description * Obtain information about eBPF programs associated with the * specified *attach_type* hook. * * The *target_fd* must be a valid file descriptor for a kernel * object which depends on the attach type of *attach_bpf_fd*: * * **BPF_PROG_TYPE_CGROUP_DEVICE**, * **BPF_PROG_TYPE_CGROUP_SKB**, * **BPF_PROG_TYPE_CGROUP_SOCK**, * **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**, * **BPF_PROG_TYPE_CGROUP_SOCKOPT**, * **BPF_PROG_TYPE_CGROUP_SYSCTL**, * **BPF_PROG_TYPE_SOCK_OPS** * * Control Group v2 hierarchy with the eBPF controller * enabled. Requires the kernel to be compiled with * **CONFIG_CGROUP_BPF**. * * **BPF_PROG_TYPE_FLOW_DISSECTOR** * * Network namespace (eg /proc/self/ns/net). * * **BPF_PROG_TYPE_LIRC_MODE2** * * LIRC device path (eg /dev/lircN). Requires the kernel * to be compiled with **CONFIG_BPF_LIRC_MODE2**. * * **BPF_PROG_QUERY** always fetches the number of programs * attached and the *attach_flags* which were used to attach those * programs. Additionally, if *prog_ids* is nonzero and the number * of attached programs is less than *prog_cnt*, populates * *prog_ids* with the eBPF program ids of the programs attached * at *target_fd*. * * The following flags may alter the result: * * **BPF_F_QUERY_EFFECTIVE** * Only return information regarding programs which are * currently effective at the specified *target_fd*. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_RAW_TRACEPOINT_OPEN * Description * Attach an eBPF program to a tracepoint *name* to access kernel * internal arguments of the tracepoint in their raw form. * * The *prog_fd* must be a valid file descriptor associated with * a loaded eBPF program of type **BPF_PROG_TYPE_RAW_TRACEPOINT**. * * No ABI guarantees are made about the content of tracepoint * arguments exposed to the corresponding eBPF program. * * Applying **close**\ (2) to the file descriptor returned by * **BPF_RAW_TRACEPOINT_OPEN** will delete the map (but see NOTES). * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_BTF_LOAD * Description * Verify and load BPF Type Format (BTF) metadata into the kernel, * returning a new file descriptor associated with the metadata. * BTF is described in more detail at * https://www.kernel.org/doc/html/latest/bpf/btf.html. * * The *btf* parameter must point to valid memory providing * *btf_size* bytes of BTF binary metadata. * * The returned file descriptor can be passed to other **bpf**\ () * subcommands such as **BPF_PROG_LOAD** or **BPF_MAP_CREATE** to * associate the BTF with those objects. * * Similar to **BPF_PROG_LOAD**, **BPF_BTF_LOAD** has optional * parameters to specify a *btf_log_buf*, *btf_log_size* and * *btf_log_level* which allow the kernel to return freeform log * output regarding the BTF verification process. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_BTF_GET_FD_BY_ID * Description * Open a file descriptor for the BPF Type Format (BTF) * corresponding to *btf_id*. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_TASK_FD_QUERY * Description * Obtain information about eBPF programs associated with the * target process identified by *pid* and *fd*. * * If the *pid* and *fd* are associated with a tracepoint, kprobe * or uprobe perf event, then the *prog_id* and *fd_type* will * be populated with the eBPF program id and file descriptor type * of type **bpf_task_fd_type**. If associated with a kprobe or * uprobe, the *probe_offset* and *probe_addr* will also be * populated. Optionally, if *buf* is provided, then up to * *buf_len* bytes of *buf* will be populated with the name of * the tracepoint, kprobe or uprobe. * * The resulting *prog_id* may be introspected in deeper detail * using **BPF_PROG_GET_FD_BY_ID** and **BPF_OBJ_GET_INFO_BY_FD**. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_MAP_LOOKUP_AND_DELETE_ELEM * Description * Look up an element with the given *key* in the map referred to * by the file descriptor *fd*, and if found, delete the element. * * For **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map * types, the *flags* argument needs to be set to 0, but for other * map types, it may be specified as: * * **BPF_F_LOCK** * Look up and delete the value of a spin-locked map * without returning the lock. This must be specified if * the elements contain a spinlock. * * The **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map types * implement this command as a "pop" operation, deleting the top * element rather than one corresponding to *key*. * The *key* and *key_len* parameters should be zeroed when * issuing this operation for these map types. * * This command is only valid for the following map types: * * **BPF_MAP_TYPE_QUEUE** * * **BPF_MAP_TYPE_STACK** * * **BPF_MAP_TYPE_HASH** * * **BPF_MAP_TYPE_PERCPU_HASH** * * **BPF_MAP_TYPE_LRU_HASH** * * **BPF_MAP_TYPE_LRU_PERCPU_HASH** * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_MAP_FREEZE * Description * Freeze the permissions of the specified map. * * Write permissions may be frozen by passing zero *flags*. * Upon success, no future syscall invocations may alter the * map state of *map_fd*. Write operations from eBPF programs * are still possible for a frozen map. * * Not supported for maps of type **BPF_MAP_TYPE_STRUCT_OPS**. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_BTF_GET_NEXT_ID * Description * Fetch the next BPF Type Format (BTF) object currently loaded * into the kernel. * * Looks for the BTF object with an id greater than *start_id* * and updates *next_id* on success. If no other BTF objects * remain with ids higher than *start_id*, returns -1 and sets * *errno* to **ENOENT**. * * Return * Returns zero on success. On error, or when no id remains, -1 * is returned and *errno* is set appropriately. * * BPF_MAP_LOOKUP_BATCH * Description * Iterate and fetch multiple elements in a map. * * Two opaque values are used to manage batch operations, * *in_batch* and *out_batch*. Initially, *in_batch* must be set * to NULL to begin the batched operation. After each subsequent * **BPF_MAP_LOOKUP_BATCH**, the caller should pass the resultant * *out_batch* as the *in_batch* for the next operation to * continue iteration from the current point. Both *in_batch* and * *out_batch* must point to memory large enough to hold a key, * except for maps of type **BPF_MAP_TYPE_{HASH, PERCPU_HASH, * LRU_HASH, LRU_PERCPU_HASH}**, for which batch parameters * must be at least 4 bytes wide regardless of key size. * * The *keys* and *values* are output parameters which must point * to memory large enough to hold *count* items based on the key * and value size of the map *map_fd*. The *keys* buffer must be * of *key_size* * *count*. The *values* buffer must be of * *value_size* * *count*. * * The *elem_flags* argument may be specified as one of the * following: * * **BPF_F_LOCK** * Look up the value of a spin-locked map without * returning the lock. This must be specified if the * elements contain a spinlock. * * On success, *count* elements from the map are copied into the * user buffer, with the keys copied into *keys* and the values * copied into the corresponding indices in *values*. * * If an error is returned and *errno* is not **EFAULT**, *count* * is set to the number of successfully processed elements. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * May set *errno* to **ENOSPC** to indicate that *keys* or * *values* is too small to dump an entire bucket during * iteration of a hash-based map type. * * BPF_MAP_LOOKUP_AND_DELETE_BATCH * Description * Iterate and delete all elements in a map. * * This operation has the same behavior as * **BPF_MAP_LOOKUP_BATCH** with two exceptions: * * * Every element that is successfully returned is also deleted * from the map. This is at least *count* elements. Note that * *count* is both an input and an output parameter. * * Upon returning with *errno* set to **EFAULT**, up to * *count* elements may be deleted without returning the keys * and values of the deleted elements. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_MAP_UPDATE_BATCH * Description * Update multiple elements in a map by *key*. * * The *keys* and *values* are input parameters which must point * to memory large enough to hold *count* items based on the key * and value size of the map *map_fd*. The *keys* buffer must be * of *key_size* * *count*. The *values* buffer must be of * *value_size* * *count*. * * Each element specified in *keys* is sequentially updated to the * value in the corresponding index in *values*. The *in_batch* * and *out_batch* parameters are ignored and should be zeroed. * * The *elem_flags* argument should be specified as one of the * following: * * **BPF_ANY** * Create new elements or update a existing elements. * **BPF_NOEXIST** * Create new elements only if they do not exist. * **BPF_EXIST** * Update existing elements. * **BPF_F_LOCK** * Update spin_lock-ed map elements. This must be * specified if the map value contains a spinlock. * * On success, *count* elements from the map are updated. * * If an error is returned and *errno* is not **EFAULT**, *count* * is set to the number of successfully processed elements. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**, or * **E2BIG**. **E2BIG** indicates that the number of elements in * the map reached the *max_entries* limit specified at map * creation time. * * May set *errno* to one of the following error codes under * specific circumstances: * * **EEXIST** * If *flags* specifies **BPF_NOEXIST** and the element * with *key* already exists in the map. * **ENOENT** * If *flags* specifies **BPF_EXIST** and the element with * *key* does not exist in the map. * * BPF_MAP_DELETE_BATCH * Description * Delete multiple elements in a map by *key*. * * The *keys* parameter is an input parameter which must point * to memory large enough to hold *count* items based on the key * size of the map *map_fd*, that is, *key_size* * *count*. * * Each element specified in *keys* is sequentially deleted. The * *in_batch*, *out_batch*, and *values* parameters are ignored * and should be zeroed. * * The *elem_flags* argument may be specified as one of the * following: * * **BPF_F_LOCK** * Look up the value of a spin-locked map without * returning the lock. This must be specified if the * elements contain a spinlock. * * On success, *count* elements from the map are updated. * * If an error is returned and *errno* is not **EFAULT**, *count* * is set to the number of successfully processed elements. If * *errno* is **EFAULT**, up to *count* elements may be been * deleted. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_LINK_CREATE * Description * Attach an eBPF program to a *target_fd* at the specified * *attach_type* hook and return a file descriptor handle for * managing the link. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_LINK_UPDATE * Description * Update the eBPF program in the specified *link_fd* to * *new_prog_fd*. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_LINK_GET_FD_BY_ID * Description * Open a file descriptor for the eBPF Link corresponding to * *link_id*. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_LINK_GET_NEXT_ID * Description * Fetch the next eBPF link currently loaded into the kernel. * * Looks for the eBPF link with an id greater than *start_id* * and updates *next_id* on success. If no other eBPF links * remain with ids higher than *start_id*, returns -1 and sets * *errno* to **ENOENT**. * * Return * Returns zero on success. On error, or when no id remains, -1 * is returned and *errno* is set appropriately. * * BPF_ENABLE_STATS * Description * Enable eBPF runtime statistics gathering. * * Runtime statistics gathering for the eBPF runtime is disabled * by default to minimize the corresponding performance overhead. * This command enables statistics globally. * * Multiple programs may independently enable statistics. * After gathering the desired statistics, eBPF runtime statistics * may be disabled again by calling **close**\ (2) for the file * descriptor returned by this function. Statistics will only be * disabled system-wide when all outstanding file descriptors * returned by prior calls for this subcommand are closed. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_ITER_CREATE * Description * Create an iterator on top of the specified *link_fd* (as * previously created using **BPF_LINK_CREATE**) and return a * file descriptor that can be used to trigger the iteration. * * If the resulting file descriptor is pinned to the filesystem * using **BPF_OBJ_PIN**, then subsequent **read**\ (2) syscalls * for that path will trigger the iterator to read kernel state * using the eBPF program attached to *link_fd*. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * BPF_LINK_DETACH * Description * Forcefully detach the specified *link_fd* from its * corresponding attachment point. * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_PROG_BIND_MAP * Description * Bind a map to the lifetime of an eBPF program. * * The map identified by *map_fd* is bound to the program * identified by *prog_fd* and only released when *prog_fd* is * released. This may be used in cases where metadata should be * associated with a program which otherwise does not contain any * references to the map (for example, embedded in the eBPF * program instructions). * * Return * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * * BPF_TOKEN_CREATE * Description * Create BPF token with embedded information about what * BPF-related functionality it allows: * - a set of allowed bpf() syscall commands; * - a set of allowed BPF map types to be created with * BPF_MAP_CREATE command, if BPF_MAP_CREATE itself is allowed; * - a set of allowed BPF program types and BPF program attach * types to be loaded with BPF_PROG_LOAD command, if * BPF_PROG_LOAD itself is allowed. * * BPF token is created (derived) from an instance of BPF FS, * assuming it has necessary delegation mount options specified. * This BPF token can be passed as an extra parameter to various * bpf() syscall commands to grant BPF subsystem functionality to * unprivileged processes. * * When created, BPF token is "associated" with the owning * user namespace of BPF FS instance (super block) that it was * derived from, and subsequent BPF operations performed with * BPF token would be performing capabilities checks (i.e., * CAP_BPF, CAP_PERFMON, CAP_NET_ADMIN, CAP_SYS_ADMIN) within * that user namespace. Without BPF token, such capabilities * have to be granted in init user namespace, making bpf() * syscall incompatible with user namespace, for the most part. * * Return * A new file descriptor (a nonnegative integer), or -1 if an * error occurred (in which case, *errno* is set appropriately). * * NOTES * eBPF objects (maps and programs) can be shared between processes. * * * After **fork**\ (2), the child inherits file descriptors * referring to the same eBPF objects. * * File descriptors referring to eBPF objects can be transferred over * **unix**\ (7) domain sockets. * * File descriptors referring to eBPF objects can be duplicated in the * usual way, using **dup**\ (2) and similar calls. * * File descriptors referring to eBPF objects can be pinned to the * filesystem using the **BPF_OBJ_PIN** command of **bpf**\ (2). * * An eBPF object is deallocated only after all file descriptors referring * to the object have been closed and no references remain pinned to the * filesystem or attached (for example, bound to a program or device). */ enum bpf_cmd { BPF_MAP_CREATE, BPF_MAP_LOOKUP_ELEM, BPF_MAP_UPDATE_ELEM, BPF_MAP_DELETE_ELEM, BPF_MAP_GET_NEXT_KEY, BPF_PROG_LOAD, BPF_OBJ_PIN, BPF_OBJ_GET, BPF_PROG_ATTACH, BPF_PROG_DETACH, BPF_PROG_TEST_RUN, BPF_PROG_RUN = BPF_PROG_TEST_RUN, BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID, BPF_PROG_GET_FD_BY_ID, BPF_MAP_GET_FD_BY_ID, BPF_OBJ_GET_INFO_BY_FD, BPF_PROG_QUERY, BPF_RAW_TRACEPOINT_OPEN, BPF_BTF_LOAD, BPF_BTF_GET_FD_BY_ID, BPF_TASK_FD_QUERY, BPF_MAP_LOOKUP_AND_DELETE_ELEM, BPF_MAP_FREEZE, BPF_BTF_GET_NEXT_ID, BPF_MAP_LOOKUP_BATCH, BPF_MAP_LOOKUP_AND_DELETE_BATCH, BPF_MAP_UPDATE_BATCH, BPF_MAP_DELETE_BATCH, BPF_LINK_CREATE, BPF_LINK_UPDATE, BPF_LINK_GET_FD_BY_ID, BPF_LINK_GET_NEXT_ID, BPF_ENABLE_STATS, BPF_ITER_CREATE, BPF_LINK_DETACH, BPF_PROG_BIND_MAP, BPF_TOKEN_CREATE, __MAX_BPF_CMD, }; 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_DEPRECATED, /* BPF_MAP_TYPE_CGROUP_STORAGE is available to bpf programs attaching * to a cgroup. The newer BPF_MAP_TYPE_CGRP_STORAGE is available to * both cgroup-attached and other progs and supports all functionality * provided by BPF_MAP_TYPE_CGROUP_STORAGE. So mark * BPF_MAP_TYPE_CGROUP_STORAGE deprecated. */ BPF_MAP_TYPE_CGROUP_STORAGE = BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED, /* BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE is available to bpf programs * attaching to a cgroup. The new mechanism (BPF_MAP_TYPE_CGRP_STORAGE + * local percpu kptr) supports all BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE * functionality and more. So mark * BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE * deprecated. */ BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED, 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, BPF_MAP_TYPE_USER_RINGBUF, BPF_MAP_TYPE_CGRP_STORAGE, BPF_MAP_TYPE_ARENA, __MAX_BPF_MAP_TYPE }; /* Note that tracing related programs such as * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT} * are not subject to a stable API since kernel internal data * structures can change from release to release and may * therefore break existing tracing BPF programs. Tracing BPF * programs correspond to /a/ specific kernel which is to be * analyzed, and not /a/ specific kernel /and/ all future ones. */ 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, /* a program that can execute syscalls */ BPF_PROG_TYPE_NETFILTER, __MAX_BPF_PROG_TYPE }; 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, BPF_CGROUP_UNIX_CONNECT, BPF_CGROUP_UNIX_SENDMSG, BPF_CGROUP_UNIX_RECVMSG, BPF_CGROUP_UNIX_GETPEERNAME, BPF_CGROUP_UNIX_GETSOCKNAME, BPF_NETKIT_PRIMARY, BPF_NETKIT_PEER, BPF_TRACE_KPROBE_SESSION, BPF_TRACE_UPROBE_SESSION, __MAX_BPF_ATTACH_TYPE }; #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE /* Add BPF_LINK_TYPE(type, name) in bpf_types.h to keep bpf_link_type_strs[] * in sync with the definitions below. */ enum bpf_link_type { BPF_LINK_TYPE_UNSPEC = 0, BPF_LINK_TYPE_RAW_TRACEPOINT = 1, BPF_LINK_TYPE_TRACING = 2, BPF_LINK_TYPE_CGROUP = 3, BPF_LINK_TYPE_ITER = 4, BPF_LINK_TYPE_NETNS = 5, BPF_LINK_TYPE_XDP = 6, BPF_LINK_TYPE_PERF_EVENT = 7, BPF_LINK_TYPE_KPROBE_MULTI = 8, BPF_LINK_TYPE_STRUCT_OPS = 9, BPF_LINK_TYPE_NETFILTER = 10, BPF_LINK_TYPE_TCX = 11, BPF_LINK_TYPE_UPROBE_MULTI = 12, BPF_LINK_TYPE_NETKIT = 13, BPF_LINK_TYPE_SOCKMAP = 14, __MAX_BPF_LINK_TYPE, }; #define MAX_BPF_LINK_TYPE __MAX_BPF_LINK_TYPE enum bpf_perf_event_type { BPF_PERF_EVENT_UNSPEC = 0, BPF_PERF_EVENT_UPROBE = 1, BPF_PERF_EVENT_URETPROBE = 2, BPF_PERF_EVENT_KPROBE = 3, BPF_PERF_EVENT_KRETPROBE = 4, BPF_PERF_EVENT_TRACEPOINT = 5, BPF_PERF_EVENT_EVENT = 6, }; /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command * * NONE(default): No further bpf programs allowed in the subtree. * * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, * the program in this cgroup yields to sub-cgroup program. * * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, * that cgroup program gets run in addition to the program in this cgroup. * * Only one program is allowed to be attached to a cgroup with * NONE or BPF_F_ALLOW_OVERRIDE flag. * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will * release old program and attach the new one. Attach flags has to match. * * Multiple programs are allowed to be attached to a cgroup with * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order * (those that were attached first, run first) * The programs of sub-cgroup are executed first, then programs of * this cgroup and then programs of parent cgroup. * When children program makes decision (like picking TCP CA or sock bind) * parent program has a chance to override it. * * With BPF_F_ALLOW_MULTI a new program is added to the end of the list of * programs for a cgroup. Though it's possible to replace an old program at * any position by also specifying BPF_F_REPLACE flag and position itself in * replace_bpf_fd attribute. Old program at this position will be released. * * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. * A cgroup with NONE doesn't allow any programs in sub-cgroups. * Ex1: * cgrp1 (MULTI progs A, B) -> * cgrp2 (OVERRIDE prog C) -> * cgrp3 (MULTI prog D) -> * cgrp4 (OVERRIDE prog E) -> * cgrp5 (NONE prog F) * the event in cgrp5 triggers execution of F,D,A,B in that order. * if prog F is detached, the execution is E,D,A,B * if prog F and D are detached, the execution is E,A,B * if prog F, E and D are detached, the execution is C,A,B * * All eligible programs are executed regardless of return code from * earlier programs. */ #define BPF_F_ALLOW_OVERRIDE (1U << 0) #define BPF_F_ALLOW_MULTI (1U << 1) /* Generic attachment flags. */ #define BPF_F_REPLACE (1U << 2) #define BPF_F_BEFORE (1U << 3) #define BPF_F_AFTER (1U << 4) #define BPF_F_ID (1U << 5) #define BPF_F_PREORDER (1U << 6) #define BPF_F_LINK BPF_F_LINK /* 1 << 13 */ /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel * has been built with CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, * and NET_IP_ALIGN defined to 2. */ #define BPF_F_STRICT_ALIGNMENT (1U << 0) /* If BPF_F_ANY_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will allow any alignment whatsoever. On platforms * with strict alignment requirements for loads ands stores (such * as sparc and mips) the verifier validates that all loads and * stores provably follow this requirement. This flag turns that * checking and enforcement off. * * It is mostly used for testing when we want to validate the * context and memory access aspects of the verifier, but because * of an unaligned access the alignment check would trigger before * the one we are interested in. */ #define BPF_F_ANY_ALIGNMENT (1U << 1) /* BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose. * Verifier does sub-register def/use analysis and identifies instructions whose * def only matters for low 32-bit, high 32-bit is never referenced later * through implicit zero extension. Therefore verifier notifies JIT back-ends * that it is safe to ignore clearing high 32-bit for these instructions. This * saves some back-ends a lot of code-gen. However such optimization is not * necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends * hence hasn't used verifier's analysis result. But, we really want to have a * way to be able to verify the correctness of the described optimization on * x86_64 on which testsuites are frequently exercised. * * So, this flag is introduced. Once it is set, verifier will randomize high * 32-bit for those instructions who has been identified as safe to ignore them. * Then, if verifier is not doing correct analysis, such randomization will * regress tests to expose bugs. */ #define BPF_F_TEST_RND_HI32 (1U << 2) /* The verifier internal test flag. Behavior is undefined */ #define BPF_F_TEST_STATE_FREQ (1U << 3) /* If BPF_F_SLEEPABLE is used in BPF_PROG_LOAD command, the verifier will * restrict map and helper usage for such programs. Sleepable BPF programs can * only be attached to hooks where kernel execution context allows sleeping. * Such programs are allowed to use helpers that may sleep like * bpf_copy_from_user(). */ #define BPF_F_SLEEPABLE (1U << 4) /* If BPF_F_XDP_HAS_FRAGS is used in BPF_PROG_LOAD command, the loaded program * fully support xdp frags. */ #define BPF_F_XDP_HAS_FRAGS (1U << 5) /* If BPF_F_XDP_DEV_BOUND_ONLY is used in BPF_PROG_LOAD command, the loaded * program becomes device-bound but can access XDP metadata. */ #define BPF_F_XDP_DEV_BOUND_ONLY (1U << 6) /* The verifier internal test flag. Behavior is undefined */ #define BPF_F_TEST_REG_INVARIANTS (1U << 7) /* link_create.kprobe_multi.flags used in LINK_CREATE command for * BPF_TRACE_KPROBE_MULTI attach type to create return probe. */ enum { BPF_F_KPROBE_MULTI_RETURN = (1U << 0) }; /* link_create.uprobe_multi.flags used in LINK_CREATE command for * BPF_TRACE_UPROBE_MULTI attach type to create return probe. */ enum { BPF_F_UPROBE_MULTI_RETURN = (1U << 0) }; /* link_create.netfilter.flags used in LINK_CREATE command for * BPF_PROG_TYPE_NETFILTER to enable IP packet defragmentation. */ #define BPF_F_NETFILTER_IP_DEFRAG (1U << 0) /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * * insn[0].src_reg: BPF_PSEUDO_MAP_[FD|IDX] * insn[0].imm: map fd or fd_idx * insn[1].imm: 0 * insn[0].off: 0 * insn[1].off: 0 * ldimm64 rewrite: address of map * verifier type: CONST_PTR_TO_MAP */ #define BPF_PSEUDO_MAP_FD 1 #define BPF_PSEUDO_MAP_IDX 5 /* insn[0].src_reg: BPF_PSEUDO_MAP_[IDX_]VALUE * insn[0].imm: map fd or fd_idx * insn[1].imm: offset into value * insn[0].off: 0 * insn[1].off: 0 * ldimm64 rewrite: address of map[0]+offset * verifier type: PTR_TO_MAP_VALUE */ #define BPF_PSEUDO_MAP_VALUE 2 #define BPF_PSEUDO_MAP_IDX_VALUE 6 /* insn[0].src_reg: BPF_PSEUDO_BTF_ID * insn[0].imm: kernel btd id of VAR * insn[1].imm: 0 * insn[0].off: 0 * insn[1].off: 0 * ldimm64 rewrite: address of the kernel variable * verifier type: PTR_TO_BTF_ID or PTR_TO_MEM, depending on whether the var * is struct/union. */ #define BPF_PSEUDO_BTF_ID 3 /* insn[0].src_reg: BPF_PSEUDO_FUNC * insn[0].imm: insn offset to the func * insn[1].imm: 0 * insn[0].off: 0 * insn[1].off: 0 * ldimm64 rewrite: address of the function * verifier type: PTR_TO_FUNC. */ #define BPF_PSEUDO_FUNC 4 /* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative * offset to another bpf function */ #define BPF_PSEUDO_CALL 1 /* when bpf_call->src_reg == BPF_PSEUDO_KFUNC_CALL, * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the running kernel */ #define BPF_PSEUDO_KFUNC_CALL 2 enum bpf_addr_space_cast { BPF_ADDR_SPACE_CAST = 1, }; /* flags for BPF_MAP_UPDATE_ELEM command */ enum { BPF_ANY = 0, /* create new element or update existing */ BPF_NOEXIST = 1, /* create new element if it didn't exist */ BPF_EXIST = 2, /* update existing element */ BPF_F_LOCK = 4, /* spin_lock-ed map_lookup/map_update */ }; /* flags for BPF_MAP_CREATE command */ enum { BPF_F_NO_PREALLOC = (1U << 0), /* Instead of having one common LRU list in the * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list * which can scale and perform better. * Note, the LRU nodes (including free nodes) cannot be moved * across different LRU lists. */ BPF_F_NO_COMMON_LRU = (1U << 1), /* Specify numa node during map creation */ BPF_F_NUMA_NODE = (1U << 2), /* Flags for accessing BPF object from syscall side. */ BPF_F_RDONLY = (1U << 3), BPF_F_WRONLY = (1U << 4), /* Flag for stack_map, store build_id+offset instead of pointer */ BPF_F_STACK_BUILD_ID = (1U << 5), /* Zero-initialize hash function seed. This should only be used for testing. */ BPF_F_ZERO_SEED = (1U << 6), /* Flags for accessing BPF object from program side. */ BPF_F_RDONLY_PROG = (1U << 7), BPF_F_WRONLY_PROG = (1U << 8), /* Clone map from listener for newly accepted socket */ BPF_F_CLONE = (1U << 9), /* Enable memory-mapping BPF map */ BPF_F_MMAPABLE = (1U << 10), /* Share perf_event among processes */ BPF_F_PRESERVE_ELEMS = (1U << 11), /* Create a map that is suitable to be an inner map with dynamic max entries */ BPF_F_INNER_MAP = (1U << 12), /* Create a map that will be registered/unregesitered by the backed bpf_link */ BPF_F_LINK = (1U << 13), /* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */ BPF_F_PATH_FD = (1U << 14), /* Flag for value_type_btf_obj_fd, the fd is available */ BPF_F_VTYPE_BTF_OBJ_FD = (1U << 15), /* BPF token FD is passed in a corresponding command's token_fd field */ BPF_F_TOKEN_FD = (1U << 16), /* When user space page faults in bpf_arena send SIGSEGV instead of inserting new page */ BPF_F_SEGV_ON_FAULT = (1U << 17), /* Do not translate kernel bpf_arena pointers to user pointers */ BPF_F_NO_USER_CONV = (1U << 18), }; /* Flags for BPF_PROG_QUERY. */ /* Query effective (directly attached + inherited from ancestor cgroups) * programs that will be executed for events within a cgroup. * attach_flags with this flag are always returned 0. */ #define BPF_F_QUERY_EFFECTIVE (1U << 0) /* Flags for BPF_PROG_TEST_RUN */ /* If set, run the test on the cpu specified by bpf_attr.test.cpu */ #define BPF_F_TEST_RUN_ON_CPU (1U << 0) /* If set, XDP frames will be transmitted after processing */ #define BPF_F_TEST_XDP_LIVE_FRAMES (1U << 1) /* If set, apply CHECKSUM_COMPLETE to skb and validate the checksum */ #define BPF_F_TEST_SKB_CHECKSUM_COMPLETE (1U << 2) /* type for BPF_ENABLE_STATS */ enum bpf_stats_type { /* enabled run_time_ns and run_cnt */ BPF_STATS_RUN_TIME = 0, }; enum bpf_stack_build_id_status { /* user space need an empty entry to identify end of a trace */ BPF_STACK_BUILD_ID_EMPTY = 0, /* with valid build_id and offset */ BPF_STACK_BUILD_ID_VALID = 1, /* couldn't get build_id, fallback to ip */ BPF_STACK_BUILD_ID_IP = 2, }; #define BPF_BUILD_ID_SIZE 20 struct bpf_stack_build_id { __s32 status; unsigned char build_id[BPF_BUILD_ID_SIZE]; union { __u64 offset; __u64 ip; }; }; #define BPF_OBJ_NAME_LEN 16U union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ __u32 key_size; /* size of key in bytes */ __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* max number of entries in a map */ __u32 map_flags; /* BPF_MAP_CREATE related * flags defined above. */ __u32 inner_map_fd; /* fd pointing to the inner map */ __u32 numa_node; /* numa node (effective only if * BPF_F_NUMA_NODE is set). */ char map_name[BPF_OBJ_NAME_LEN]; __u32 map_ifindex; /* ifindex of netdev to create on */ __u32 btf_fd; /* fd pointing to a BTF type data */ __u32 btf_key_type_id; /* BTF type_id of the key */ __u32 btf_value_type_id; /* BTF type_id of the value */ __u32 btf_vmlinux_value_type_id;/* BTF type_id of a kernel- * struct stored as the * map value */ /* Any per-map-type extra fields * * BPF_MAP_TYPE_BLOOM_FILTER - the lowest 4 bits indicate the * number of hash functions (if 0, the bloom filter will default * to using 5 hash functions). * * BPF_MAP_TYPE_ARENA - contains the address where user space * is going to mmap() the arena. It has to be page aligned. */ __u64 map_extra; __s32 value_type_btf_obj_fd; /* fd pointing to a BTF * type data for * btf_vmlinux_value_type_id. */ /* BPF token FD to use with BPF_MAP_CREATE operation. * If provided, map_flags should have BPF_F_TOKEN_FD flag set. */ __s32 map_token_fd; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */ __u32 map_fd; __aligned_u64 key; union { __aligned_u64 value; __aligned_u64 next_key; }; __u64 flags; }; struct { /* struct used by BPF_MAP_*_BATCH commands */ __aligned_u64 in_batch; /* start batch, * NULL to start from beginning */ __aligned_u64 out_batch; /* output: next start batch */ __aligned_u64 keys; __aligned_u64 values; __u32 count; /* input/output: * input: # of key/value * elements * output: # of filled elements */ __u32 map_fd; __u64 elem_flags; __u64 flags; } batch; struct { /* anonymous struct used by BPF_PROG_LOAD command */ __u32 prog_type; /* one of enum bpf_prog_type */ __u32 insn_cnt; __aligned_u64 insns; __aligned_u64 license; __u32 log_level; /* verbosity level of verifier */ __u32 log_size; /* size of user buffer */ __aligned_u64 log_buf; /* user supplied buffer */ __u32 kern_version; /* not used */ __u32 prog_flags; char prog_name[BPF_OBJ_NAME_LEN]; __u32 prog_ifindex; /* ifindex of netdev to prep for */ /* For some prog types expected attach type must be known at * load time to verify attach type specific parts of prog * (context accesses, allowed helpers, etc). */ __u32 expected_attach_type; __u32 prog_btf_fd; /* fd pointing to BTF type data */ __u32 func_info_rec_size; /* userspace bpf_func_info size */ __aligned_u64 func_info; /* func info */ __u32 func_info_cnt; /* number of bpf_func_info records */ __u32 line_info_rec_size; /* userspace bpf_line_info size */ __aligned_u64 line_info; /* line info */ __u32 line_info_cnt; /* number of bpf_line_info records */ __u32 attach_btf_id; /* in-kernel BTF type id to attach to */ union { /* valid prog_fd to attach to bpf prog */ __u32 attach_prog_fd; /* or valid module BTF object fd or 0 to attach to vmlinux */ __u32 attach_btf_obj_fd; }; __u32 core_relo_cnt; /* number of bpf_core_relo */ __aligned_u64 fd_array; /* array of FDs */ __aligned_u64 core_relos; __u32 core_relo_rec_size; /* sizeof(struct bpf_core_relo) */ /* output: actual total log contents size (including termintaing zero). * It could be both larger than original log_size (if log was * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 log_true_size; /* BPF token FD to use with BPF_PROG_LOAD operation. * If provided, prog_flags should have BPF_F_TOKEN_FD flag set. */ __s32 prog_token_fd; /* The fd_array_cnt can be used to pass the length of the * fd_array array. In this case all the [map] file descriptors * passed in this array will be bound to the program, even if * the maps are not referenced directly. The functionality is * similar to the BPF_PROG_BIND_MAP syscall, but maps can be * used by the verifier during the program load. If provided, * then the fd_array[0,...,fd_array_cnt-1] is expected to be * continuous. */ __u32 fd_array_cnt; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ __aligned_u64 pathname; __u32 bpf_fd; __u32 file_flags; /* Same as dirfd in openat() syscall; see openat(2) * manpage for details of path FD and pathname semantics; * path_fd should accompanied by BPF_F_PATH_FD flag set in * file_flags field, otherwise it should be set to zero; * if BPF_F_PATH_FD flag is not set, AT_FDCWD is assumed. */ __s32 path_fd; }; struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ union { __u32 target_fd; /* target object to attach to or ... */ __u32 target_ifindex; /* target ifindex */ }; __u32 attach_bpf_fd; __u32 attach_type; __u32 attach_flags; __u32 replace_bpf_fd; union { __u32 relative_fd; __u32 relative_id; }; __u64 expected_revision; }; struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ __u32 prog_fd; __u32 retval; __u32 data_size_in; /* input: len of data_in */ __u32 data_size_out; /* input/output: len of data_out * returns ENOSPC if data_out * is too small. */ __aligned_u64 data_in; __aligned_u64 data_out; __u32 repeat; __u32 duration; __u32 ctx_size_in; /* input: len of ctx_in */ __u32 ctx_size_out; /* input/output: len of ctx_out * returns ENOSPC if ctx_out * is too small. */ __aligned_u64 ctx_in; __aligned_u64 ctx_out; __u32 flags; __u32 cpu; __u32 batch_size; } test; struct { /* anonymous struct used by BPF_*_GET_*_ID */ union { __u32 start_id; __u32 prog_id; __u32 map_id; __u32 btf_id; __u32 link_id; }; __u32 next_id; __u32 open_flags; __s32 fd_by_id_token_fd; }; struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */ __u32 bpf_fd; __u32 info_len; __aligned_u64 info; } info; struct { /* anonymous struct used by BPF_PROG_QUERY command */ union { __u32 target_fd; /* target object to query or ... */ __u32 target_ifindex; /* target ifindex */ }; __u32 attach_type; __u32 query_flags; __u32 attach_flags; __aligned_u64 prog_ids; union { __u32 prog_cnt; __u32 count; }; __u32 :32; /* output: per-program attach_flags. * not allowed to be set during effective query. */ __aligned_u64 prog_attach_flags; __aligned_u64 link_ids; __aligned_u64 link_attach_flags; __u64 revision; } query; struct { /* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */ __u64 name; __u32 prog_fd; __u32 :32; __aligned_u64 cookie; } raw_tracepoint; struct { /* anonymous struct for BPF_BTF_LOAD */ __aligned_u64 btf; __aligned_u64 btf_log_buf; __u32 btf_size; __u32 btf_log_size; __u32 btf_log_level; /* output: actual total log contents size (including termintaing zero). * It could be both larger than original log_size (if log was * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 btf_log_true_size; __u32 btf_flags; /* BPF token FD to use with BPF_BTF_LOAD operation. * If provided, btf_flags should have BPF_F_TOKEN_FD flag set. */ __s32 btf_token_fd; }; struct { __u32 pid; /* input: pid */ __u32 fd; /* input: fd */ __u32 flags; /* input: flags */ __u32 buf_len; /* input/output: buf len */ __aligned_u64 buf; /* input/output: * tp_name for tracepoint * symbol for kprobe * filename for uprobe */ __u32 prog_id; /* output: prod_id */ __u32 fd_type; /* output: BPF_FD_TYPE_* */ __u64 probe_offset; /* output: probe_offset */ __u64 probe_addr; /* output: probe_addr */ } task_fd_query; struct { /* struct used by BPF_LINK_CREATE command */ union { __u32 prog_fd; /* eBPF program to attach */ __u32 map_fd; /* struct_ops to attach */ }; union { __u32 target_fd; /* target object to attach to or ... */ __u32 target_ifindex; /* target ifindex */ }; __u32 attach_type; /* attach type */ __u32 flags; /* extra flags */ union { __u32 target_btf_id; /* btf_id of target to attach to */ struct { __aligned_u64 iter_info; /* extra bpf_iter_link_info */ __u32 iter_info_len; /* iter_info length */ }; struct { /* black box user-provided value passed through * to BPF program at the execution time and * accessible through bpf_get_attach_cookie() BPF helper */ __u64 bpf_cookie; } perf_event; struct { __u32 flags; __u32 cnt; __aligned_u64 syms; __aligned_u64 addrs; __aligned_u64 cookies; } kprobe_multi; struct { /* this is overlaid with the target_btf_id above. */ __u32 target_btf_id; /* black box user-provided value passed through * to BPF program at the execution time and * accessible through bpf_get_attach_cookie() BPF helper */ __u64 cookie; } tracing; struct { __u32 pf; __u32 hooknum; __s32 priority; __u32 flags; } netfilter; struct { union { __u32 relative_fd; __u32 relative_id; }; __u64 expected_revision; } tcx; struct { __aligned_u64 path; __aligned_u64 offsets; __aligned_u64 ref_ctr_offsets; __aligned_u64 cookies; __u32 cnt; __u32 flags; __u32 pid; } uprobe_multi; struct { union { __u32 relative_fd; __u32 relative_id; }; __u64 expected_revision; } netkit; }; } link_create; struct { /* struct used by BPF_LINK_UPDATE command */ __u32 link_fd; /* link fd */ union { /* new program fd to update link with */ __u32 new_prog_fd; /* new struct_ops map fd to update link with */ __u32 new_map_fd; }; __u32 flags; /* extra flags */ union { /* expected link's program fd; is specified only if * BPF_F_REPLACE flag is set in flags. */ __u32 old_prog_fd; /* expected link's map fd; is specified only * if BPF_F_REPLACE flag is set. */ __u32 old_map_fd; }; } link_update; struct { __u32 link_fd; } link_detach; struct { /* struct used by BPF_ENABLE_STATS command */ __u32 type; } enable_stats; struct { /* struct used by BPF_ITER_CREATE command */ __u32 link_fd; __u32 flags; } iter_create; struct { /* struct used by BPF_PROG_BIND_MAP command */ __u32 prog_fd; __u32 map_fd; __u32 flags; /* extra flags */ } prog_bind_map; struct { /* struct used by BPF_TOKEN_CREATE command */ __u32 flags; __u32 bpffs_fd; } token_create; } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF * developers about the multiple available eBPF helper functions. It can be * parsed and used to produce a manual page. The workflow is the following, * and requires the rst2man utility: * * $ ./scripts/bpf_doc.py \ * --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst * $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7 * $ man /tmp/bpf-helpers.7 * * Note that in order to produce this external documentation, some RST * formatting is used in the descriptions to get "bold" and "italics" in * manual pages. Also note that the few trailing white spaces are * intentional, removing them would break paragraphs for rst2man. * * Start of BPF helper function descriptions: * * void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) * Description * Perform a lookup in *map* for an entry associated to *key*. * Return * Map value associated to *key*, or **NULL** if no entry was * found. * * long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) * Description * Add or update the value of the entry associated to *key* in * *map* with *value*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * Flag value **BPF_NOEXIST** cannot be used for maps of types * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all * elements always exist), the helper would return an error. * Return * 0 on success, or a negative error in case of failure. * * long bpf_map_delete_elem(struct bpf_map *map, const void *key) * Description * Delete entry with *key* from *map*. * Return * 0 on success, or a negative error in case of failure. * * long bpf_probe_read(void *dst, u32 size, const void *unsafe_ptr) * Description * For tracing programs, safely attempt to read *size* bytes from * kernel space address *unsafe_ptr* and store the data in *dst*. * * Generally, use **bpf_probe_read_user**\ () or * **bpf_probe_read_kernel**\ () instead. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_ktime_get_ns(void) * Description * Return the time elapsed since system boot, in nanoseconds. * Does not include time the system was suspended. * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) * Return * Current *ktime*. * * long bpf_trace_printk(const char *fmt, u32 fmt_size, ...) * Description * This helper is a "printk()-like" facility for debugging. It * prints a message defined by format *fmt* (of size *fmt_size*) * to file *\/sys/kernel/tracing/trace* from TraceFS, if * available. It can take up to three additional **u64** * arguments (as an eBPF helpers, the total number of arguments is * limited to five). * * Each time the helper is called, it appends a line to the trace. * Lines are discarded while *\/sys/kernel/tracing/trace* is * open, use *\/sys/kernel/tracing/trace_pipe* to avoid this. * The format of the trace is customizable, and the exact output * one will get depends on the options set in * *\/sys/kernel/tracing/trace_options* (see also the * *README* file under the same directory). However, it usually * defaults to something like: * * :: * * telnet-470 [001] .N.. 419421.045894: 0x00000001: * * In the above: * * * ``telnet`` is the name of the current task. * * ``470`` is the PID of the current task. * * ``001`` is the CPU number on which the task is * running. * * In ``.N..``, each character refers to a set of * options (whether irqs are enabled, scheduling * options, whether hard/softirqs are running, level of * preempt_disabled respectively). **N** means that * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** * are set. * * ``419421.045894`` is a timestamp. * * ``0x00000001`` is a fake value used by BPF for the * instruction pointer register. * * ```` is the message formatted with * *fmt*. * * The conversion specifiers supported by *fmt* are similar, but * more limited than for printk(). They are **%d**, **%i**, * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size * of field, padding with zeroes, etc.) is available, and the * helper will return **-EINVAL** (but print nothing) if it * encounters an unknown specifier. * * Also, note that **bpf_trace_printk**\ () is slow, and should * only be used for debugging purposes. For this reason, a notice * block (spanning several lines) is printed to kernel logs and * states that the helper should not be used "for production use" * the first time this helper is used (or more precisely, when * **trace_printk**\ () buffers are allocated). For passing values * to user space, perf events should be preferred. * Return * The number of bytes written to the buffer, or a negative error * in case of failure. * * u32 bpf_get_prandom_u32(void) * Description * Get a pseudo-random number. * * From a security point of view, this helper uses its own * pseudo-random internal state, and cannot be used to infer the * seed of other random functions in the kernel. However, it is * essential to note that the generator used by the helper is not * cryptographically secure. * Return * A random 32-bit unsigned value. * * u32 bpf_get_smp_processor_id(void) * Description * Get the SMP (symmetric multiprocessing) processor id. Note that * all programs run with migration disabled, which means that the * SMP processor id is stable during all the execution of the * program. * Return * The SMP id of the processor running the program. * Attributes * __bpf_fastcall * * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) * Description * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. The *flags* are a combination * of the following values: * * **BPF_F_RECOMPUTE_CSUM** * Automatically update *skb*\ **->csum** after storing the * bytes. * **BPF_F_INVALIDATE_HASH** * Set *skb*\ **->hash**, *skb*\ **->swhash** and *skb*\ * **->l4hash** to 0. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_l3_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 size) * Description * Recompute the layer 3 (e.g. IP) checksum for the packet * associated to *skb*. Computation is incremental, so the helper * must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored in *size*. * Alternatively, it is possible to store the difference between * the previous and the new values of the header field in *to*, by * setting *from* and *size* to 0. For both methods, *offset* * indicates the location of the IP checksum within the packet. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags) * Description * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the * packet associated to *skb*. Computation is incremental, so the * helper must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored on the lowest * four bits of *flags*. Alternatively, it is possible to store * the difference between the previous and the new values of the * header field in *to*, by setting *from* and the four lowest * bits of *flags* to 0. For both methods, *offset* indicates the * location of the IP checksum within the packet. In addition to * the size of the field, *flags* can be added (bitwise OR) actual * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and * for updates resulting in a null checksum the value is set to * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates * that the modified header field is part of the pseudo-header. * Flag **BPF_F_IPV6** should be set for IPv6 packets. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index) * Description * This special helper is used to trigger a "tail call", or in * other words, to jump into another eBPF program. The same stack * frame is used (but values on stack and in registers for the * caller are not accessible to the callee). This mechanism allows * for program chaining, either for raising the maximum number of * available eBPF instructions, or to execute given programs in * conditional blocks. For security reasons, there is an upper * limit to the number of successive tail calls that can be * performed. * * Upon call of this helper, the program attempts to jump into a * program referenced at index *index* in *prog_array_map*, a * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes * *ctx*, a pointer to the context. * * If the call succeeds, the kernel immediately runs the first * instruction of the new program. This is not a function call, * and it never returns to the previous program. If the call * fails, then the helper has no effect, and the caller continues * to run its subsequent instructions. A call can fail if the * destination program for the jump does not exist (i.e. *index* * is superior to the number of entries in *prog_array_map*), or * if the maximum number of tail calls has been reached for this * chain of programs. This limit is defined in the kernel by the * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), * which is currently set to 33. * Return * 0 on success, or a negative error in case of failure. * * long bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags) * Description * Clone and redirect the packet associated to *skb* to another * net device of index *ifindex*. Both ingress and egress * interfaces can be used for redirection. The **BPF_F_INGRESS** * value in *flags* is used to make the distinction (ingress path * is selected if the flag is present, egress path otherwise). * This is the only flag supported for now. * * In comparison with **bpf_redirect**\ () helper, * **bpf_clone_redirect**\ () has the associated cost of * duplicating the packet buffer, but this can be executed out of * the eBPF program. Conversely, **bpf_redirect**\ () is more * efficient, but it is handled through an action code where the * redirection happens only after the eBPF program has returned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. Positive * error indicates a potential drop or congestion in the target * device. The particular positive error codes are not defined. * * u64 bpf_get_current_pid_tgid(void) * Description * Get the current pid and tgid. * Return * A 64-bit integer containing the current tgid and pid, and * created as such: * *current_task*\ **->tgid << 32 \|** * *current_task*\ **->pid**. * * u64 bpf_get_current_uid_gid(void) * Description * Get the current uid and gid. * Return * A 64-bit integer containing the current GID and UID, and * created as such: *current_gid* **<< 32 \|** *current_uid*. * * long bpf_get_current_comm(void *buf, u32 size_of_buf) * Description * Copy the **comm** attribute of the current task into *buf* of * *size_of_buf*. The **comm** attribute contains the name of * the executable (excluding the path) for the current task. The * *size_of_buf* must be strictly positive. On success, the * helper makes sure that the *buf* is NUL-terminated. On failure, * it is filled with zeroes. * Return * 0 on success, or a negative error in case of failure. * * u32 bpf_get_cgroup_classid(struct sk_buff *skb) * Description * Retrieve the classid for the current task, i.e. for the net_cls * cgroup to which *skb* belongs. * * This helper can be used on TC egress path, but not on ingress. * * The net_cls cgroup provides an interface to tag network packets * based on a user-provided identifier for all traffic coming from * the tasks belonging to the related cgroup. See also the related * kernel documentation, available from the Linux sources in file * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. * * The Linux kernel has two versions for cgroups: there are * cgroups v1 and cgroups v2. Both are available to users, who can * use a mixture of them, but note that the net_cls cgroup is for * cgroup v1 only. This makes it incompatible with BPF programs * run on cgroups, which is a cgroup-v2-only feature (a socket can * only hold data for one version of cgroups at a time). * * This helper is only available is the kernel was compiled with * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to * "**y**" or to "**m**". * Return * The classid, or 0 for the default unconfigured classid. * * long bpf_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) * Description * Push a *vlan_tci* (VLAN tag control information) of protocol * *vlan_proto* to the packet associated to *skb*, then update * the checksum. Note that if *vlan_proto* is different from * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to * be **ETH_P_8021Q**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_vlan_pop(struct sk_buff *skb) * Description * Pop a VLAN header from the packet associated to *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_get_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) * Description * Get tunnel metadata. This helper takes a pointer *key* to an * empty **struct bpf_tunnel_key** of **size**, that will be * filled with tunnel metadata for the packet associated to *skb*. * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which * indicates that the tunnel is based on IPv6 protocol instead of * IPv4. * * The **struct bpf_tunnel_key** is an object that generalizes the * principal parameters used by various tunneling protocols into a * single struct. This way, it can be used to easily make a * decision based on the contents of the encapsulation header, * "summarized" in this struct. In particular, it holds the IP * address of the remote end (IPv4 or IPv6, depending on the case) * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, * this struct exposes the *key*\ **->tunnel_id**, which is * generally mapped to a VNI (Virtual Network Identifier), making * it programmable together with the **bpf_skb_set_tunnel_key**\ * () helper. * * Let's imagine that the following code is part of a program * attached to the TC ingress interface, on one end of a GRE * tunnel, and is supposed to filter out all messages coming from * remote ends with IPv4 address other than 10.0.0.1: * * :: * * int ret; * struct bpf_tunnel_key key = {}; * * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); * if (ret < 0) * return TC_ACT_SHOT; // drop packet * * if (key.remote_ipv4 != 0x0a000001) * return TC_ACT_SHOT; // drop packet * * return TC_ACT_OK; // accept packet * * This interface can also be used with all encapsulation devices * that can operate in "collect metadata" mode: instead of having * one network device per specific configuration, the "collect * metadata" mode only requires a single device where the * configuration can be extracted from this helper. * * This can be used together with various tunnels such as VXLan, * Geneve, GRE or IP in IP (IPIP). * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_set_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) * Description * Populate tunnel metadata for packet associated to *skb.* The * tunnel metadata is set to the contents of *key*, of *size*. The * *flags* can be set to a combination of the following values: * * **BPF_F_TUNINFO_IPV6** * Indicate that the tunnel is based on IPv6 protocol * instead of IPv4. * **BPF_F_ZERO_CSUM_TX** * For IPv4 packets, add a flag to tunnel metadata * indicating that checksum computation should be skipped * and checksum set to zeroes. * **BPF_F_DONT_FRAGMENT** * Add a flag to tunnel metadata indicating that the * packet should not be fragmented. * **BPF_F_SEQ_NUMBER** * Add a flag to tunnel metadata indicating that a * sequence number should be added to tunnel header before * sending the packet. This flag was added for GRE * encapsulation, but might be used with other protocols * as well in the future. * **BPF_F_NO_TUNNEL_KEY** * Add a flag to tunnel metadata indicating that no tunnel * key should be set in the resulting tunnel header. * * Here is a typical usage on the transmit path: * * :: * * struct bpf_tunnel_key key; * populate key ... * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); * * See also the description of the **bpf_skb_get_tunnel_key**\ () * helper for additional information. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_perf_event_read(struct bpf_map *map, u64 flags) * Description * Read the value of a perf event counter. This helper relies on a * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of * the perf event counter is selected when *map* is updated with * perf event file descriptors. The *map* is an array whose size * is the number of available CPUs, and each cell contains a value * relative to one CPU. The value to retrieve is indicated by * *flags*, that contains the index of the CPU to look up, masked * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * Note that before Linux 4.13, only hardware perf event can be * retrieved. * * Also, be aware that the newer helper * **bpf_perf_event_read_value**\ () is recommended over * **bpf_perf_event_read**\ () in general. The latter has some ABI * quirks where error and counter value are used as a return code * (which is wrong to do since ranges may overlap). This issue is * fixed with **bpf_perf_event_read_value**\ (), which at the same * time provides more features over the **bpf_perf_event_read**\ * () interface. Please refer to the description of * **bpf_perf_event_read_value**\ () for details. * Return * The value of the perf event counter read from the map, or a * negative error code in case of failure. * * long bpf_redirect(u32 ifindex, u64 flags) * Description * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_clone_redirect**\ * (), except that the packet is not cloned, which provides * increased performance. * * Except for XDP, both ingress and egress interfaces can be used * for redirection. The **BPF_F_INGRESS** value in *flags* is used * to make the distinction (ingress path is selected if the flag * is present, egress path otherwise). Currently, XDP only * supports redirection to the egress interface, and accepts no * flag at all. * * The same effect can also be attained with the more generic * **bpf_redirect_map**\ (), which uses a BPF map to store the * redirect target instead of providing it directly to the helper. * Return * For XDP, the helper returns **XDP_REDIRECT** on success or * **XDP_ABORTED** on error. For other program types, the values * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on * error. * * u32 bpf_get_route_realm(struct sk_buff *skb) * Description * Retrieve the realm or the route, that is to say the * **tclassid** field of the destination for the *skb*. The * identifier retrieved is a user-provided tag, similar to the * one used with the net_cls cgroup (see description for * **bpf_get_cgroup_classid**\ () helper), but here this tag is * held by a route (a destination entry), not by a task. * * Retrieving this identifier works with the clsact TC egress hook * (see also **tc-bpf(8)**), or alternatively on conventional * classful egress qdiscs, but not on TC ingress path. In case of * clsact TC egress hook, this has the advantage that, internally, * the destination entry has not been dropped yet in the transmit * path. Therefore, the destination entry does not need to be * artificially held via **netif_keep_dst**\ () for a classful * qdisc until the *skb* is freed. * * This helper is available only if the kernel was compiled with * **CONFIG_IP_ROUTE_CLASSID** configuration option. * Return * The realm of the route for the packet associated to *skb*, or 0 * if none was found. * * long bpf_perf_event_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) * Description * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * The context of the program *ctx* needs also be passed to the * helper. * * On user space, a program willing to read the values needs to * call **perf_event_open**\ () on the perf event (either for * one or for all CPUs) and to store the file descriptor into the * *map*. This must be done before the eBPF program can send data * into it. An example is available in file * *samples/bpf/trace_output_user.c* in the Linux kernel source * tree (the eBPF program counterpart is in * *samples/bpf/trace_output_kern.c*). * * **bpf_perf_event_output**\ () achieves better performance * than **bpf_trace_printk**\ () for sharing data with user * space, and is much better suitable for streaming data from eBPF * programs. * * Note that this helper is not restricted to tracing use cases * and can be used with programs attached to TC or XDP as well, * where it allows for passing data to user space listeners. Data * can be: * * * Only custom structs, * * Only the packet payload, or * * A combination of both. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_load_bytes(const void *skb, u32 offset, void *to, u32 len) * Description * This helper was provided as an easy way to load data from a * packet. It can be used to load *len* bytes from *offset* from * the packet associated to *skb*, into the buffer pointed by * *to*. * * Since Linux 4.7, usage of this helper has mostly been replaced * by "direct packet access", enabling packet data to be * manipulated with *skb*\ **->data** and *skb*\ **->data_end** * pointing respectively to the first byte of packet data and to * the byte after the last byte of packet data. However, it * remains useful if one wishes to read large quantities of data * at once from a packet into the eBPF stack. * Return * 0 on success, or a negative error in case of failure. * * long bpf_get_stackid(void *ctx, struct bpf_map *map, u64 flags) * Description * Walk a user or a kernel stack and return its id. To achieve * this, the helper needs *ctx*, which is a pointer to the context * on which the tracing program is executed, and a pointer to a * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * a combination of the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_FAST_STACK_CMP** * Compare stacks by hash only. * **BPF_F_REUSE_STACKID** * If two different stacks hash into the same *stackid*, * discard the old one. * * The stack id retrieved is a 32 bit long integer handle which * can be further combined with other data (including other stack * ids) and used as a key into maps. This can be useful for * generating a variety of graphs (such as flame graphs or off-cpu * graphs). * * For walking a stack, this helper is an improvement over * **bpf_probe_read**\ (), which can be used with unrolled loops * but is not efficient and consumes a lot of eBPF instructions. * Instead, **bpf_get_stackid**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * Return * The positive or null stack id on success, or a negative error * in case of failure. * * s64 bpf_csum_diff(__be32 *from, u32 from_size, __be32 *to, u32 to_size, __wsum seed) * Description * Compute a checksum difference, from the raw buffer pointed by * *from*, of length *from_size* (that must be a multiple of 4), * towards the raw buffer pointed by *to*, of size *to_size* * (same remark). An optional *seed* can be added to the value * (this can be cascaded, the seed may come from a previous call * to the helper). * * This is flexible enough to be used in several ways: * * * With *from_size* == 0, *to_size* > 0 and *seed* set to * checksum, it can be used when pushing new data. * * With *from_size* > 0, *to_size* == 0 and *seed* set to * checksum, it can be used when removing data from a packet. * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it * can be used to compute a diff. Note that *from_size* and * *to_size* do not need to be equal. * * This helper can be used in combination with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to * which one can feed in the difference computed with * **bpf_csum_diff**\ (). * Return * The checksum result, or a negative error code in case of * failure. * * long bpf_skb_get_tunnel_opt(struct sk_buff *skb, void *opt, u32 size) * Description * Retrieve tunnel options metadata for the packet associated to * *skb*, and store the raw tunnel option data to the buffer *opt* * of *size*. * * This helper can be used with encapsulation devices that can * operate in "collect metadata" mode (please refer to the related * note in the description of **bpf_skb_get_tunnel_key**\ () for * more details). A particular example where this can be used is * in combination with the Geneve encapsulation protocol, where it * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) * and retrieving arbitrary TLVs (Type-Length-Value headers) from * the eBPF program. This allows for full customization of these * headers. * Return * The size of the option data retrieved. * * long bpf_skb_set_tunnel_opt(struct sk_buff *skb, void *opt, u32 size) * Description * Set tunnel options metadata for the packet associated to *skb* * to the option data contained in the raw buffer *opt* of *size*. * * See also the description of the **bpf_skb_get_tunnel_opt**\ () * helper for additional information. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_change_proto(struct sk_buff *skb, __be16 proto, u64 flags) * Description * Change the protocol of the *skb* to *proto*. Currently * supported are transition from IPv4 to IPv6, and from IPv6 to * IPv4. The helper takes care of the groundwork for the * transition, including resizing the socket buffer. The eBPF * program is expected to fill the new headers, if any, via * **skb_store_bytes**\ () and to recompute the checksums with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ * (). The main case for this helper is to perform NAT64 * operations out of an eBPF program. * * Internally, the GSO type is marked as dodgy so that headers are * checked and segments are recalculated by the GSO/GRO engine. * The size for GSO target is adapted as well. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_change_type(struct sk_buff *skb, u32 type) * Description * Change the packet type for the packet associated to *skb*. This * comes down to setting *skb*\ **->pkt_type** to *type*, except * the eBPF program does not have a write access to *skb*\ * **->pkt_type** beside this helper. Using a helper here allows * for graceful handling of errors. * * The major use case is to change incoming *skb*s to * **PACKET_HOST** in a programmatic way instead of having to * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for * example. * * Note that *type* only allows certain values. At this time, they * are: * * **PACKET_HOST** * Packet is for us. * **PACKET_BROADCAST** * Send packet to all. * **PACKET_MULTICAST** * Send packet to group. * **PACKET_OTHERHOST** * Send packet to someone else. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_under_cgroup(struct sk_buff *skb, struct bpf_map *map, u32 index) * Description * Check whether *skb* is a descendant of the cgroup2 held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * Return * The return value depends on the result of the test, and can be: * * * 0, if the *skb* failed the cgroup2 descendant test. * * 1, if the *skb* succeeded the cgroup2 descendant test. * * A negative error code, if an error occurred. * * u32 bpf_get_hash_recalc(struct sk_buff *skb) * Description * Retrieve the hash of the packet, *skb*\ **->hash**. If it is * not set, in particular if the hash was cleared due to mangling, * recompute this hash. Later accesses to the hash can be done * directly with *skb*\ **->hash**. * * Calling **bpf_set_hash_invalid**\ (), changing a packet * prototype with **bpf_skb_change_proto**\ (), or calling * **bpf_skb_store_bytes**\ () with the * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear * the hash and to trigger a new computation for the next call to * **bpf_get_hash_recalc**\ (). * Return * The 32-bit hash. * * u64 bpf_get_current_task(void) * Description * Get the current task. * Return * A pointer to the current task struct. * * long bpf_probe_write_user(void *dst, const void *src, u32 len) * Description * Attempt in a safe way to write *len* bytes from the buffer * *src* to *dst* in memory. It only works for threads that are in * user context, and *dst* must be a valid user space address. * * This helper should not be used to implement any kind of * security mechanism because of TOC-TOU attacks, but rather to * debug, divert, and manipulate execution of semi-cooperative * processes. * * Keep in mind that this feature is meant for experiments, and it * has a risk of crashing the system and running programs. * Therefore, when an eBPF program using this helper is attached, * a warning including PID and process name is printed to kernel * logs. * Return * 0 on success, or a negative error in case of failure. * * long bpf_current_task_under_cgroup(struct bpf_map *map, u32 index) * Description * Check whether the probe is being run is the context of a given * subset of the cgroup2 hierarchy. The cgroup2 to test is held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * Return * The return value depends on the result of the test, and can be: * * * 1, if current task belongs to the cgroup2. * * 0, if current task does not belong to the cgroup2. * * A negative error code, if an error occurred. * * long bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags) * Description * Resize (trim or grow) the packet associated to *skb* to the * new *len*. The *flags* are reserved for future usage, and must * be left at zero. * * The basic idea is that the helper performs the needed work to * change the size of the packet, then the eBPF program rewrites * the rest via helpers like **bpf_skb_store_bytes**\ (), * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () * and others. This helper is a slow path utility intended for * replies with control messages. And because it is targeted for * slow path, the helper itself can afford to be slow: it * implicitly linearizes, unclones and drops offloads from the * *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_pull_data(struct sk_buff *skb, u32 len) * Description * Pull in non-linear data in case the *skb* is non-linear and not * all of *len* are part of the linear section. Make *len* bytes * from *skb* readable and writable. If a zero value is passed for * *len*, then all bytes in the linear part of *skb* will be made * readable and writable. * * This helper is only needed for reading and writing with direct * packet access. * * For direct packet access, testing that offsets to access * are within packet boundaries (test on *skb*\ **->data_end**) is * susceptible to fail if offsets are invalid, or if the requested * data is in non-linear parts of the *skb*. On failure the * program can just bail out, or in the case of a non-linear * buffer, use a helper to make the data available. The * **bpf_skb_load_bytes**\ () helper is a first solution to access * the data. Another one consists in using **bpf_skb_pull_data** * to pull in once the non-linear parts, then retesting and * eventually access the data. * * At the same time, this also makes sure the *skb* is uncloned, * which is a necessary condition for direct write. As this needs * to be an invariant for the write part only, the verifier * detects writes and adds a prologue that is calling * **bpf_skb_pull_data()** to effectively unclone the *skb* from * the very beginning in case it is indeed cloned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * s64 bpf_csum_update(struct sk_buff *skb, __wsum csum) * Description * Add the checksum *csum* into *skb*\ **->csum** in case the * driver has supplied a checksum for the entire packet into that * field. Return an error otherwise. This helper is intended to be * used in combination with **bpf_csum_diff**\ (), in particular * when the checksum needs to be updated after data has been * written into the packet through direct packet access. * Return * The checksum on success, or a negative error code in case of * failure. * * void bpf_set_hash_invalid(struct sk_buff *skb) * Description * Invalidate the current *skb*\ **->hash**. It can be used after * mangling on headers through direct packet access, in order to * indicate that the hash is outdated and to trigger a * recalculation the next time the kernel tries to access this * hash or when the **bpf_get_hash_recalc**\ () helper is called. * Return * void. * * long bpf_get_numa_node_id(void) * Description * Return the id of the current NUMA node. The primary use case * for this helper is the selection of sockets for the local NUMA * node, when the program is attached to sockets using the * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), * but the helper is also available to other eBPF program types, * similarly to **bpf_get_smp_processor_id**\ (). * Return * The id of current NUMA node. * * long bpf_skb_change_head(struct sk_buff *skb, u32 len, u64 flags) * Description * Grows headroom of packet associated to *skb* and adjusts the * offset of the MAC header accordingly, adding *len* bytes of * space. It automatically extends and reallocates memory as * required. * * This helper can be used on a layer 3 *skb* to push a MAC header * for redirection into a layer 2 device. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta) * Description * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that * it is possible to use a negative value for *delta*. This helper * can be used to prepare the packet for pushing or popping * headers. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_probe_read_str(void *dst, u32 size, const void *unsafe_ptr) * Description * Copy a NUL terminated string from an unsafe kernel address * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for * more details. * * Generally, use **bpf_probe_read_user_str**\ () or * **bpf_probe_read_kernel_str**\ () instead. * Return * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. * * u64 bpf_get_socket_cookie(struct sk_buff *skb) * Description * If the **struct sk_buff** pointed by *skb* has a known socket, * retrieve the cookie (generated by the kernel) of this socket. * If no cookie has been set yet, generate a new cookie. Once * generated, the socket cookie remains stable for the life of the * socket. This helper can be useful for monitoring per socket * networking traffic statistics as it provides a global socket * identifier that can be assumed unique. * Return * A 8-byte long unique number on success, or 0 if the socket * field is missing inside *skb*. * * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx) * Description * Equivalent to bpf_get_socket_cookie() helper that accepts * *skb*, but gets socket from **struct bpf_sock_addr** context. * Return * A 8-byte long unique number. * * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx) * Description * Equivalent to **bpf_get_socket_cookie**\ () helper that accepts * *skb*, but gets socket from **struct bpf_sock_ops** context. * Return * A 8-byte long unique number. * * u64 bpf_get_socket_cookie(struct sock *sk) * Description * Equivalent to **bpf_get_socket_cookie**\ () helper that accepts * *sk*, but gets socket from a BTF **struct sock**. This helper * also works for sleepable programs. * Return * A 8-byte long unique number or 0 if *sk* is NULL. * * u32 bpf_get_socket_uid(struct sk_buff *skb) * Description * Get the owner UID of the socked associated to *skb*. * Return * The owner UID of the socket associated to *skb*. If the socket * is **NULL**, or if it is not a full socket (i.e. if it is a * time-wait or a request socket instead), **overflowuid** value * is returned (note that **overflowuid** might also be the actual * UID value for the socket). * * long bpf_set_hash(struct sk_buff *skb, u32 hash) * Description * Set the full hash for *skb* (set the field *skb*\ **->hash**) * to value *hash*. * Return * 0 * * long bpf_setsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen) * Description * Emulate a call to **setsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **setsockopt(2)** for more information. * The option value of length *optlen* is pointed by *optval*. * * *bpf_socket* should be one of the following: * * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**, * **BPF_CGROUP_INET6_CONNECT** and **BPF_CGROUP_UNIX_CONNECT**. * * This helper actually implements a subset of **setsockopt()**. * It supports the following *level*\ s: * * * **SOL_SOCKET**, which supports the following *optname*\ s: * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, * **SO_BINDTODEVICE**, **SO_KEEPALIVE**, **SO_REUSEADDR**, * **SO_REUSEPORT**, **SO_BINDTOIFINDEX**, **SO_TXREHASH**. * * **IPPROTO_TCP**, which supports the following *optname*\ s: * **TCP_CONGESTION**, **TCP_BPF_IW**, * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**, * **TCP_NODELAY**, **TCP_MAXSEG**, **TCP_WINDOW_CLAMP**, * **TCP_THIN_LINEAR_TIMEOUTS**, **TCP_BPF_DELACK_MAX**, * **TCP_BPF_RTO_MIN**, **TCP_BPF_SOCK_OPS_CB_FLAGS**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports the following *optname*\ s: * **IPV6_TCLASS**, **IPV6_AUTOFLOWLABEL**. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_adjust_room(struct sk_buff *skb, s32 len_diff, u32 mode, u64 flags) * Description * Grow or shrink the room for data in the packet associated to * *skb* by *len_diff*, and according to the selected *mode*. * * By default, the helper will reset any offloaded checksum * indicator of the skb to CHECKSUM_NONE. This can be avoided * by the following flag: * * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded * checksum data of the skb to CHECKSUM_NONE. * * There are two supported modes at this time: * * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer * (room space is added or removed between the layer 2 and * layer 3 headers). * * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer * (room space is added or removed between the layer 3 and * layer 4 headers). * * The following flags are supported at this time: * * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. * Adjusting mss in this way is not allowed for datagrams. * * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: * Any new space is reserved to hold a tunnel header. * Configure skb offsets and other fields accordingly. * * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: * Use with ENCAP_L3 flags to further specify the tunnel type. * * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): * Use with ENCAP_L3/L4 flags to further specify the tunnel * type; *len* is the length of the inner MAC header. * * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**: * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the * L2 type as Ethernet. * * * **BPF_F_ADJ_ROOM_DECAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_DECAP_L3_IPV6**: * Indicate the new IP header version after decapsulating the outer * IP header. Used when the inner and outer IP versions are different. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_redirect_map(struct bpf_map *map, u64 key, u64 flags) * Description * Redirect the packet to the endpoint referenced by *map* at * index *key*. Depending on its type, this *map* can contain * references to net devices (for forwarding packets through other * ports), or to CPUs (for redirecting XDP frames to another CPU; * but this is only implemented for native XDP (with driver * support) as of this writing). * * The lower two bits of *flags* are used as the return code if * the map lookup fails. This is so that the return value can be * one of the XDP program return codes up to **XDP_TX**, as chosen * by the caller. The higher bits of *flags* can be set to * BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below. * * With BPF_F_BROADCAST the packet will be broadcasted to all the * interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress * interface will be excluded when do broadcasting. * * See also **bpf_redirect**\ (), which only supports redirecting * to an ifindex, but doesn't require a map to do so. * Return * **XDP_REDIRECT** on success, or the value of the two lower bits * of the *flags* argument on error. * * long bpf_sk_redirect_map(struct sk_buff *skb, struct bpf_map *map, u32 key, u64 flags) * Description * Redirect the packet to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * long bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) * Description * Add an entry to, or update a *map* referencing sockets. The * *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * Return * 0 on success, or a negative error in case of failure. * * long bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta) * Description * Adjust the address pointed by *xdp_md*\ **->data_meta** by * *delta* (which can be positive or negative). Note that this * operation modifies the address stored in *xdp_md*\ **->data**, * so the latter must be loaded only after the helper has been * called. * * The use of *xdp_md*\ **->data_meta** is optional and programs * are not required to use it. The rationale is that when the * packet is processed with XDP (e.g. as DoS filter), it is * possible to push further meta data along with it before passing * to the stack, and to give the guarantee that an ingress eBPF * program attached as a TC classifier on the same device can pick * this up for further post-processing. Since TC works with socket * buffers, it remains possible to set from XDP the **mark** or * **priority** pointers, or other pointers for the socket buffer. * Having this scratch space generic and programmable allows for * more flexibility as the user is free to store whatever meta * data they need. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_perf_event_read_value(struct bpf_map *map, u64 flags, struct bpf_perf_event_value *buf, u32 buf_size) * Description * Read the value of a perf event counter, and store it into *buf* * of size *buf_size*. This helper relies on a *map* of type * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event * counter is selected when *map* is updated with perf event file * descriptors. The *map* is an array whose size is the number of * available CPUs, and each cell contains a value relative to one * CPU. The value to retrieve is indicated by *flags*, that * contains the index of the CPU to look up, masked with * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * This helper behaves in a way close to * **bpf_perf_event_read**\ () helper, save that instead of * just returning the value observed, it fills the *buf* * structure. This allows for additional data to be retrieved: in * particular, the enabled and running times (in *buf*\ * **->enabled** and *buf*\ **->running**, respectively) are * copied. In general, **bpf_perf_event_read_value**\ () is * recommended over **bpf_perf_event_read**\ (), which has some * ABI issues and provides fewer functionalities. * * These values are interesting, because hardware PMU (Performance * Monitoring Unit) counters are limited resources. When there are * more PMU based perf events opened than available counters, * kernel will multiplex these events so each event gets certain * percentage (but not all) of the PMU time. In case that * multiplexing happens, the number of samples or counter value * will not reflect the case compared to when no multiplexing * occurs. This makes comparison between different runs difficult. * Typically, the counter value should be normalized before * comparing to other experiments. The usual normalization is done * as follows. * * :: * * normalized_counter = counter * t_enabled / t_running * * Where t_enabled is the time enabled for event and t_running is * the time running for event since last normalization. The * enabled and running times are accumulated since the perf event * open. To achieve scaling factor between two invocations of an * eBPF program, users can use CPU id as the key (which is * typical for perf array usage model) to remember the previous * value and do the calculation inside the eBPF program. * Return * 0 on success, or a negative error in case of failure. * * long bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, u32 buf_size) * Description * For an eBPF program attached to a perf event, retrieve the * value of the event counter associated to *ctx* and store it in * the structure pointed by *buf* and of size *buf_size*. Enabled * and running times are also stored in the structure (see * description of helper **bpf_perf_event_read_value**\ () for * more details). * Return * 0 on success, or a negative error in case of failure. * * long bpf_getsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen) * Description * Emulate a call to **getsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **getsockopt(2)** for more information. * The retrieved value is stored in the structure pointed by * *opval* and of length *optlen*. * * *bpf_socket* should be one of the following: * * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**, * **BPF_CGROUP_INET6_CONNECT** and **BPF_CGROUP_UNIX_CONNECT**. * * This helper actually implements a subset of **getsockopt()**. * It supports the same set of *optname*\ s that is supported by * the **bpf_setsockopt**\ () helper. The exceptions are * **TCP_BPF_*** is **bpf_setsockopt**\ () only and * **TCP_SAVED_SYN** is **bpf_getsockopt**\ () only. * Return * 0 on success, or a negative error in case of failure. * * long bpf_override_return(struct pt_regs *regs, u64 rc) * Description * Used for error injection, this helper uses kprobes to override * the return value of the probed function, and to set it to *rc*. * The first argument is the context *regs* on which the kprobe * works. * * This helper works by setting the PC (program counter) * to an override function which is run in place of the original * probed function. This means the probed function is not run at * all. The replacement function just returns with the required * value. * * This helper has security implications, and thus is subject to * restrictions. It is only available if the kernel was compiled * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration * option, and in this case it only works on functions tagged with * **ALLOW_ERROR_INJECTION** in the kernel code. * Return * 0 * * long bpf_sock_ops_cb_flags_set(struct bpf_sock_ops *bpf_sock, int argval) * Description * Attempt to set the value of the **bpf_sock_ops_cb_flags** field * for the full TCP socket associated to *bpf_sock_ops* to * *argval*. * * The primary use of this field is to determine if there should * be calls to eBPF programs of type * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP * code. A program of the same type can change its value, per * connection and as necessary, when the connection is * established. This field is directly accessible for reading, but * this helper must be used for updates in order to return an * error if an eBPF program tries to set a callback that is not * supported in the current kernel. * * *argval* is a flag array which can combine these flags: * * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) * * Therefore, this function can be used to clear a callback flag by * setting the appropriate bit to zero. e.g. to disable the RTO * callback: * * **bpf_sock_ops_cb_flags_set(bpf_sock,** * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** * * Here are some examples of where one could call such eBPF * program: * * * When RTO fires. * * When a packet is retransmitted. * * When the connection terminates. * * When a packet is sent. * * When a packet is received. * Return * Code **-EINVAL** if the socket is not a full TCP socket; * otherwise, a positive number containing the bits that could not * be set is returned (which comes down to 0 if all bits were set * as required). * * long bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags) * Description * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * long bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes) * Description * For socket policies, apply the verdict of the eBPF program to * the next *bytes* (number of bytes) of message *msg*. * * For example, this helper can be used in the following cases: * * * A single **sendmsg**\ () or **sendfile**\ () system call * contains multiple logical messages that the eBPF program is * supposed to read and for which it should apply a verdict. * * An eBPF program only cares to read the first *bytes* of a * *msg*. If the message has a large payload, then setting up * and calling the eBPF program repeatedly for all bytes, even * though the verdict is already known, would create unnecessary * overhead. * * When called from within an eBPF program, the helper sets a * counter internal to the BPF infrastructure, that is used to * apply the last verdict to the next *bytes*. If *bytes* is * smaller than the current data being processed from a * **sendmsg**\ () or **sendfile**\ () system call, the first * *bytes* will be sent and the eBPF program will be re-run with * the pointer for start of data pointing to byte number *bytes* * **+ 1**. If *bytes* is larger than the current data being * processed, then the eBPF verdict will be applied to multiple * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are * consumed. * * Note that if a socket closes with the internal counter holding * a non-zero value, this is not a problem because data is not * being buffered for *bytes* and is sent as it is received. * Return * 0 * * long bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes) * Description * For socket policies, prevent the execution of the verdict eBPF * program for message *msg* until *bytes* (byte number) have been * accumulated. * * This can be used when one needs a specific number of bytes * before a verdict can be assigned, even if the data spans * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme * case would be a user calling **sendmsg**\ () repeatedly with * 1-byte long message segments. Obviously, this is bad for * performance, but it is still valid. If the eBPF program needs * *bytes* bytes to validate a header, this helper can be used to * prevent the eBPF program to be called again until *bytes* have * been accumulated. * Return * 0 * * long bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags) * Description * For socket policies, pull in non-linear data from user space * for *msg* and set pointers *msg*\ **->data** and *msg*\ * **->data_end** to *start* and *end* bytes offsets into *msg*, * respectively. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it can only parse data that the (**data**, **data_end**) * pointers have already consumed. For **sendmsg**\ () hooks this * is likely the first scatterlist element. But for calls relying * on the **sendpage** handler (e.g. **sendfile**\ ()) this will * be the range (**0**, **0**) because the data is shared with * user space and by default the objective is to avoid allowing * user space to modify data while (or after) eBPF verdict is * being decided. This helper can be used to pull in data and to * set the start and end pointer to given values. Data will be * copied if necessary (i.e. if data was not linear and if start * and end pointers do not point to the same chunk). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * All values for *flags* are reserved for future usage, and must * be left at zero. * Return * 0 on success, or a negative error in case of failure. * * long bpf_bind(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) * Description * Bind the socket associated to *ctx* to the address pointed by * *addr*, of length *addr_len*. This allows for making outgoing * connection from the desired IP address, which can be useful for * example when all processes inside a cgroup should use one * single IP address on a host that has multiple IP configured. * * This helper works for IPv4 and IPv6, TCP and UDP sockets. The * domain (*addr*\ **->sa_family**) must be **AF_INET** (or * **AF_INET6**). It's advised to pass zero port (**sin_port** * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like * behavior and lets the kernel efficiently pick up an unused * port as long as 4-tuple is unique. Passing non-zero port might * lead to degraded performance. * Return * 0 on success, or a negative error in case of failure. * * long bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta) * Description * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is * possible to both shrink and grow the packet tail. * Shrink done via *delta* being a negative integer. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_skb_get_xfrm_state(struct sk_buff *skb, u32 index, struct bpf_xfrm_state *xfrm_state, u32 size, u64 flags) * Description * Retrieve the XFRM state (IP transform framework, see also * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. * * The retrieved value is stored in the **struct bpf_xfrm_state** * pointed by *xfrm_state* and of length *size*. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_XFRM** configuration option. * Return * 0 on success, or a negative error in case of failure. * * long bpf_get_stack(void *ctx, void *buf, u32 size, u64 flags) * Description * Return a user or a kernel stack in bpf program provided buffer. * To achieve this, the helper needs *ctx*, which is a pointer * to the context on which the tracing program is executed. * To store the stacktrace, the bpf program provides *buf* with * a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_USER_BUILD_ID** * Collect (build_id, file_offset) instead of ips for user * stack, only valid if **BPF_F_USER_STACK** is also * specified. * * *file_offset* is an offset relative to the beginning * of the executable or shared object file backing the vma * which the *ip* falls in. It is *not* an offset relative * to that object's base address. Accordingly, it must be * adjusted by adding (sh_addr - sh_offset), where * sh_{addr,offset} correspond to the executable section * containing *file_offset* in the object, for comparisons * to symbols' st_value to be valid. * * **bpf_get_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * Return * The non-negative copied *buf* length equal to or less than * *size* on success, or a negative error in case of failure. * * long bpf_skb_load_bytes_relative(const void *skb, u32 offset, void *to, u32 len, u32 start_header) * Description * This helper is similar to **bpf_skb_load_bytes**\ () in that * it provides an easy way to load *len* bytes from *offset* * from the packet associated to *skb*, into the buffer pointed * by *to*. The difference to **bpf_skb_load_bytes**\ () is that * a fifth argument *start_header* exists in order to select a * base offset to start from. *start_header* can be one of: * * **BPF_HDR_START_MAC** * Base offset to load data from is *skb*'s mac header. * **BPF_HDR_START_NET** * Base offset to load data from is *skb*'s network header. * * In general, "direct packet access" is the preferred method to * access packet data, however, this helper is in particular useful * in socket filters where *skb*\ **->data** does not always point * to the start of the mac header and where "direct packet access" * is not available. * Return * 0 on success, or a negative error in case of failure. * * long bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags) * Description * Do FIB lookup in kernel tables using parameters in *params*. * If lookup is successful and result shows packet is to be * forwarded, the neighbor tables are searched for the nexthop. * If successful (ie., FIB lookup shows forwarding and nexthop * is resolved), the nexthop address is returned in ipv4_dst * or ipv6_dst based on family, smac is set to mac address of * egress device, dmac is set to nexthop mac address, rt_metric * is set to metric from route (IPv4/IPv6 only), and ifindex * is set to the device index of the nexthop from the FIB lookup. * * *plen* argument is the size of the passed in struct. * *flags* argument can be a combination of one or more of the * following values: * * **BPF_FIB_LOOKUP_DIRECT** * Do a direct table lookup vs full lookup using FIB * rules. * **BPF_FIB_LOOKUP_TBID** * Used with BPF_FIB_LOOKUP_DIRECT. * Use the routing table ID present in *params*->tbid * for the fib lookup. * **BPF_FIB_LOOKUP_OUTPUT** * Perform lookup from an egress perspective (default is * ingress). * **BPF_FIB_LOOKUP_SKIP_NEIGH** * Skip the neighbour table lookup. *params*->dmac * and *params*->smac will not be set as output. A common * use case is to call **bpf_redirect_neigh**\ () after * doing **bpf_fib_lookup**\ (). * **BPF_FIB_LOOKUP_SRC** * Derive and set source IP addr in *params*->ipv{4,6}_src * for the nexthop. If the src addr cannot be derived, * **BPF_FIB_LKUP_RET_NO_SRC_ADDR** is returned. In this * case, *params*->dmac and *params*->smac are not set either. * **BPF_FIB_LOOKUP_MARK** * Use the mark present in *params*->mark for the fib lookup. * This option should not be used with BPF_FIB_LOOKUP_DIRECT, * as it only has meaning for full lookups. * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** tc cls_act programs. * Return * * < 0 if any input argument is invalid * * 0 on success (packet is forwarded, nexthop neighbor exists) * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the * packet is not forwarded or needs assist from full stack * * If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU * was exceeded and output params->mtu_result contains the MTU. * * long bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) * Description * Add an entry to, or update a sockhash *map* referencing sockets. * The *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * Return * 0 on success, or a negative error in case of failure. * * long bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags) * Description * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * long bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags) * Description * This helper is used in programs implementing policies at the * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. * if the verdict eBPF program returns **SK_PASS**), redirect it * to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * long bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len) * Description * Encapsulate the packet associated to *skb* within a Layer 3 * protocol header. This header is provided in the buffer at * address *hdr*, with *len* its size in bytes. *type* indicates * the protocol of the header and can be one of: * * **BPF_LWT_ENCAP_SEG6** * IPv6 encapsulation with Segment Routing Header * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, * the IPv6 header is computed by the kernel. * **BPF_LWT_ENCAP_SEG6_INLINE** * Only works if *skb* contains an IPv6 packet. Insert a * Segment Routing Header (**struct ipv6_sr_hdr**) inside * the IPv6 header. * **BPF_LWT_ENCAP_IP** * IP encapsulation (GRE/GUE/IPIP/etc). The outer header * must be IPv4 or IPv6, followed by zero or more * additional headers, up to **LWT_BPF_MAX_HEADROOM** * total bytes in all prepended headers. Please note that * if **skb_is_gso**\ (*skb*) is true, no more than two * headers can be prepended, and the inner header, if * present, should be either GRE or UDP/GUE. * * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and * **BPF_PROG_TYPE_LWT_XMIT**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len) * Description * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. Only the flags, tag and TLVs * inside the outermost IPv6 Segment Routing Header can be * modified through this helper. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta) * Description * Adjust the size allocated to TLVs in the outermost IPv6 * Segment Routing Header contained in the packet associated to * *skb*, at position *offset* by *delta* bytes. Only offsets * after the segments are accepted. *delta* can be as well * positive (growing) as negative (shrinking). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len) * Description * Apply an IPv6 Segment Routing action of type *action* to the * packet associated to *skb*. Each action takes a parameter * contained at address *param*, and of length *param_len* bytes. * *action* can be one of: * * **SEG6_LOCAL_ACTION_END_X** * End.X action: Endpoint with Layer-3 cross-connect. * Type of *param*: **struct in6_addr**. * **SEG6_LOCAL_ACTION_END_T** * End.T action: Endpoint with specific IPv6 table lookup. * Type of *param*: **int**. * **SEG6_LOCAL_ACTION_END_B6** * End.B6 action: Endpoint bound to an SRv6 policy. * Type of *param*: **struct ipv6_sr_hdr**. * **SEG6_LOCAL_ACTION_END_B6_ENCAP** * End.B6.Encap action: Endpoint bound to an SRv6 * encapsulation policy. * Type of *param*: **struct ipv6_sr_hdr**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * long bpf_rc_repeat(void *ctx) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded repeat key message. This delays * the generation of a key up event for previously generated * key down event. * * Some IR protocols like NEC have a special IR message for * repeating last button, for when a button is held down. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * long bpf_rc_keydown(void *ctx, u32 protocol, u64 scancode, u32 toggle) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded key press with *scancode*, * *toggle* value in the given *protocol*. The scancode will be * translated to a keycode using the rc keymap, and reported as * an input key down event. After a period a key up event is * generated. This period can be extended by calling either * **bpf_rc_keydown**\ () again with the same values, or calling * **bpf_rc_repeat**\ (). * * Some protocols include a toggle bit, in case the button was * released and pressed again between consecutive scancodes. * * The *ctx* should point to the lirc sample as passed into * the program. * * The *protocol* is the decoded protocol number (see * **enum rc_proto** for some predefined values). * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * u64 bpf_skb_cgroup_id(struct sk_buff *skb) * Description * Return the cgroup v2 id of the socket associated with the *skb*. * This is roughly similar to the **bpf_get_cgroup_classid**\ () * helper for cgroup v1 by providing a tag resp. identifier that * can be matched on or used for map lookups e.g. to implement * policy. The cgroup v2 id of a given path in the hierarchy is * exposed in user space through the f_handle API in order to get * to the same 64-bit id. * * This helper can be used on TC egress path, but not on ingress, * and is available only if the kernel was compiled with the * **CONFIG_SOCK_CGROUP_DATA** configuration option. * Return * The id is returned or 0 in case the id could not be retrieved. * * u64 bpf_get_current_cgroup_id(void) * Description * Get the current cgroup id based on the cgroup within which * the current task is running. * Return * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. * * void *bpf_get_local_storage(void *map, u64 flags) * Description * Get the pointer to the local storage area. * The type and the size of the local storage is defined * by the *map* argument. * The *flags* meaning is specific for each map type, * and has to be 0 for cgroup local storage. * * Depending on the BPF program type, a local storage area * can be shared between multiple instances of the BPF program, * running simultaneously. * * A user should care about the synchronization by himself. * For example, by using the **BPF_ATOMIC** instructions to alter * the shared data. * Return * A pointer to the local storage area. * * long bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags) * Description * Select a **SO_REUSEPORT** socket from a * **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*. * It checks the selected socket is matching the incoming * request in the socket buffer. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level) * Description * Return id of cgroup v2 that is ancestor of cgroup associated * with the *skb* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *skb*, then return value will be same as that * of **bpf_skb_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *skb*. * * The format of returned id and helper limitations are same as in * **bpf_skb_cgroup_id**\ (). * Return * The id is returned or 0 in case the id could not be retrieved. * * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) * Description * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * Return * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. * * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) * Description * Look for UDP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * Return * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. * * long bpf_sk_release(void *sock) * Description * Release the reference held by *sock*. *sock* must be a * non-**NULL** pointer that was returned from * **bpf_sk_lookup_xxx**\ (). * Return * 0 on success, or a negative error in case of failure. * * long bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags) * Description * Push an element *value* in *map*. *flags* is one of: * * **BPF_EXIST** * If the queue/stack is full, the oldest element is * removed to make room for this. * Return * 0 on success, or a negative error in case of failure. * * long bpf_map_pop_elem(struct bpf_map *map, void *value) * Description * Pop an element from *map*. * Return * 0 on success, or a negative error in case of failure. * * long bpf_map_peek_elem(struct bpf_map *map, void *value) * Description * Get an element from *map* without removing it. * Return * 0 on success, or a negative error in case of failure. * * long bpf_msg_push_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags) * Description * For socket policies, insert *len* bytes into *msg* at offset * *start*. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it may want to insert metadata or options into the *msg*. * This can later be read and used by any of the lower layer BPF * hooks. * * This helper may fail if under memory pressure (a malloc * fails) in these cases BPF programs will get an appropriate * error and BPF programs will need to handle them. * Return * 0 on success, or a negative error in case of failure. * * long bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags) * Description * Will remove *len* bytes from a *msg* starting at byte *start*. * This may result in **ENOMEM** errors under certain situations if * an allocation and copy are required due to a full ring buffer. * However, the helper will try to avoid doing the allocation * if possible. Other errors can occur if input parameters are * invalid either due to *start* byte not being valid part of *msg* * payload and/or *pop* value being to large. * Return * 0 on success, or a negative error in case of failure. * * long bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded pointer movement. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * long bpf_spin_lock(struct bpf_spin_lock *lock) * Description * Acquire a spinlock represented by the pointer *lock*, which is * stored as part of a value of a map. Taking the lock allows to * safely update the rest of the fields in that value. The * spinlock can (and must) later be released with a call to * **bpf_spin_unlock**\ (\ *lock*\ ). * * Spinlocks in BPF programs come with a number of restrictions * and constraints: * * * **bpf_spin_lock** objects are only allowed inside maps of * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this * list could be extended in the future). * * BTF description of the map is mandatory. * * The BPF program can take ONE lock at a time, since taking two * or more could cause dead locks. * * Only one **struct bpf_spin_lock** is allowed per map element. * * When the lock is taken, calls (either BPF to BPF or helpers) * are not allowed. * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not * allowed inside a spinlock-ed region. * * The BPF program MUST call **bpf_spin_unlock**\ () to release * the lock, on all execution paths, before it returns. * * The BPF program can access **struct bpf_spin_lock** only via * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () * helpers. Loading or storing data into the **struct * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. * * To use the **bpf_spin_lock**\ () helper, the BTF description * of the map value must be a struct and have **struct * bpf_spin_lock** *anyname*\ **;** field at the top level. * Nested lock inside another struct is not allowed. * * The **struct bpf_spin_lock** *lock* field in a map value must * be aligned on a multiple of 4 bytes in that value. * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy * the **bpf_spin_lock** field to user space. * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from * a BPF program, do not update the **bpf_spin_lock** field. * * **bpf_spin_lock** cannot be on the stack or inside a * networking packet (it can only be inside of a map values). * * **bpf_spin_lock** is available to root only. * * Tracing programs and socket filter programs cannot use * **bpf_spin_lock**\ () due to insufficient preemption checks * (but this may change in the future). * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. * Return * 0 * * long bpf_spin_unlock(struct bpf_spin_lock *lock) * Description * Release the *lock* previously locked by a call to * **bpf_spin_lock**\ (\ *lock*\ ). * Return * 0 * * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk) * Description * This helper gets a **struct bpf_sock** pointer such * that all the fields in this **bpf_sock** can be accessed. * Return * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. * * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk) * Description * This helper gets a **struct bpf_tcp_sock** pointer from a * **struct bpf_sock** pointer. * Return * A **struct bpf_tcp_sock** pointer on success, or **NULL** in * case of failure. * * long bpf_skb_ecn_set_ce(struct sk_buff *skb) * Description * Set ECN (Explicit Congestion Notification) field of IP header * to **CE** (Congestion Encountered) if current value is **ECT** * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 * and IPv4. * Return * 1 if the **CE** flag is set (either by the current helper call * or because it was already present), 0 if it is not set. * * struct bpf_sock *bpf_get_listener_sock(struct bpf_sock *sk) * Description * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. * **bpf_sk_release**\ () is unnecessary and not allowed. * Return * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. * * struct bpf_sock *bpf_skc_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) * Description * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * This function is identical to **bpf_sk_lookup_tcp**\ (), except * that it also returns timewait or request sockets. Use * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the * full structure. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * Return * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. * * long bpf_tcp_check_syncookie(void *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) * Description * Check whether *iph* and *th* contain a valid SYN cookie ACK for * the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ipv6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * Return * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative * error otherwise. * * long bpf_sysctl_get_name(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags) * Description * Get name of sysctl in /proc/sys/ and copy it into provided by * program buffer *buf* of size *buf_len*. * * The buffer is always NUL terminated, unless it's zero-sized. * * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name * only (e.g. "tcp_mem"). * Return * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * long bpf_sysctl_get_current_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len) * Description * Get current value of sysctl as it is presented in /proc/sys * (incl. newline, etc), and copy it as a string into provided * by program buffer *buf* of size *buf_len*. * * The whole value is copied, no matter what file position user * space issued e.g. sys_read at. * * The buffer is always NUL terminated, unless it's zero-sized. * Return * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if current value was unavailable, e.g. because * sysctl is uninitialized and read returns -EIO for it. * * long bpf_sysctl_get_new_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len) * Description * Get new value being written by user space to sysctl (before * the actual write happens) and copy it as a string into * provided by program buffer *buf* of size *buf_len*. * * User space may write new value at file position > 0. * * The buffer is always NUL terminated, unless it's zero-sized. * Return * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if sysctl is being read. * * long bpf_sysctl_set_new_value(struct bpf_sysctl *ctx, const char *buf, size_t buf_len) * Description * Override new value being written by user space to sysctl with * value provided by program in buffer *buf* of size *buf_len*. * * *buf* should contain a string in same form as provided by user * space on sysctl write. * * User space may write new value at file position > 0. To override * the whole sysctl value file position should be set to zero. * Return * 0 on success. * * **-E2BIG** if the *buf_len* is too big. * * **-EINVAL** if sysctl is being read. * * long bpf_strtol(const char *buf, size_t buf_len, u64 flags, long *res) * Description * Convert the initial part of the string from buffer *buf* of * size *buf_len* to a long integer according to the given base * and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)) followed by a single * optional '**-**' sign. * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtol**\ (3). * Return * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. * * long bpf_strtoul(const char *buf, size_t buf_len, u64 flags, unsigned long *res) * Description * Convert the initial part of the string from buffer *buf* of * size *buf_len* to an unsigned long integer according to the * given base and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)). * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtoul**\ (3). * Return * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. * * void *bpf_sk_storage_get(struct bpf_map *map, void *sk, void *value, u64 flags) * Description * Get a bpf-local-storage from a *sk*. * * Logically, it could be thought of getting the value from * a *map* with *sk* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this * helper enforces the key must be a full socket and the map must * be a **BPF_MAP_TYPE_SK_STORAGE** also. * * Underneath, the value is stored locally at *sk* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf-local-storages residing at *sk*. * * *sk* is a kernel **struct sock** pointer for LSM program. * *sk* is a **struct bpf_sock** pointer for other program types. * * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be * used such that a new bpf-local-storage will be * created if one does not exist. *value* can be used * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf-local-storage. If *value* is * **NULL**, the new bpf-local-storage will be zero initialized. * Return * A bpf-local-storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf-local-storage. * * long bpf_sk_storage_delete(struct bpf_map *map, void *sk) * Description * Delete a bpf-local-storage from a *sk*. * Return * 0 on success. * * **-ENOENT** if the bpf-local-storage cannot be found. * **-EINVAL** if sk is not a fullsock (e.g. a request_sock). * * long bpf_send_signal(u32 sig) * Description * Send signal *sig* to the process of the current task. * The signal may be delivered to any of this process's threads. * Return * 0 on success or successfully queued. * * **-EBUSY** if work queue under nmi is full. * * **-EINVAL** if *sig* is invalid. * * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. * * s64 bpf_tcp_gen_syncookie(void *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) * Description * Try to issue a SYN cookie for the packet with corresponding * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ipv6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header with options (at least * **sizeof**\ (**struct tcphdr**)). * Return * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** SYN cookie cannot be issued due to error * * **-ENOENT** SYN cookie should not be issued (no SYN flood) * * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies * * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 * * long bpf_skb_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) * Description * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * *ctx* is a pointer to in-kernel struct sk_buff. * * This helper is similar to **bpf_perf_event_output**\ () but * restricted to raw_tracepoint bpf programs. * Return * 0 on success, or a negative error in case of failure. * * long bpf_probe_read_user(void *dst, u32 size, const void *unsafe_ptr) * Description * Safely attempt to read *size* bytes from user space address * *unsafe_ptr* and store the data in *dst*. * Return * 0 on success, or a negative error in case of failure. * * long bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr) * Description * Safely attempt to read *size* bytes from kernel space address * *unsafe_ptr* and store the data in *dst*. * Return * 0 on success, or a negative error in case of failure. * * long bpf_probe_read_user_str(void *dst, u32 size, const void *unsafe_ptr) * Description * Copy a NUL terminated string from an unsafe user address * *unsafe_ptr* to *dst*. The *size* should include the * terminating NUL byte. In case the string length is smaller than * *size*, the target is not padded with further NUL bytes. If the * string length is larger than *size*, just *size*-1 bytes are * copied and the last byte is set to NUL. * * On success, returns the number of bytes that were written, * including the terminal NUL. This makes this helper useful in * tracing programs for reading strings, and more importantly to * get its length at runtime. See the following snippet: * * :: * * SEC("kprobe/sys_open") * void bpf_sys_open(struct pt_regs *ctx) * { * char buf[PATHLEN]; // PATHLEN is defined to 256 * int res = bpf_probe_read_user_str(buf, sizeof(buf), * ctx->di); * * // Consume buf, for example push it to * // userspace via bpf_perf_event_output(); we * // can use res (the string length) as event * // size, after checking its boundaries. * } * * In comparison, using **bpf_probe_read_user**\ () helper here * instead to read the string would require to estimate the length * at compile time, and would often result in copying more memory * than necessary. * * Another useful use case is when parsing individual process * arguments or individual environment variables navigating * *current*\ **->mm->arg_start** and *current*\ * **->mm->env_start**: using this helper and the return value, * one can quickly iterate at the right offset of the memory area. * Return * On success, the strictly positive length of the output string, * including the trailing NUL character. On error, a negative * value. * * long bpf_probe_read_kernel_str(void *dst, u32 size, const void *unsafe_ptr) * Description * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. * Return * On success, the strictly positive length of the string, including * the trailing NUL character. On error, a negative value. * * long bpf_tcp_send_ack(void *tp, u32 rcv_nxt) * Description * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. * *rcv_nxt* is the ack_seq to be sent out. * Return * 0 on success, or a negative error in case of failure. * * long bpf_send_signal_thread(u32 sig) * Description * Send signal *sig* to the thread corresponding to the current task. * Return * 0 on success or successfully queued. * * **-EBUSY** if work queue under nmi is full. * * **-EINVAL** if *sig* is invalid. * * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. * * u64 bpf_jiffies64(void) * Description * Obtain the 64bit jiffies * Return * The 64 bit jiffies * * long bpf_read_branch_records(struct bpf_perf_event_data *ctx, void *buf, u32 size, u64 flags) * Description * For an eBPF program attached to a perf event, retrieve the * branch records (**struct perf_branch_entry**) associated to *ctx* * and store it in the buffer pointed by *buf* up to size * *size* bytes. * Return * On success, number of bytes written to *buf*. On error, a * negative value. * * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to * instead return the number of bytes required to store all the * branch entries. If this flag is set, *buf* may be NULL. * * **-EINVAL** if arguments invalid or **size** not a multiple * of **sizeof**\ (**struct perf_branch_entry**\ ). * * **-ENOENT** if architecture does not support branch records. * * long bpf_get_ns_current_pid_tgid(u64 dev, u64 ino, struct bpf_pidns_info *nsdata, u32 size) * Description * Returns 0 on success, values for *pid* and *tgid* as seen from the current * *namespace* will be returned in *nsdata*. * Return * 0 on success, or one of the following in case of failure: * * **-EINVAL** if dev and inum supplied don't match dev_t and inode number * with nsfs of current task, or if dev conversion to dev_t lost high bits. * * **-ENOENT** if pidns does not exists for the current task. * * long bpf_xdp_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) * Description * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * *ctx* is a pointer to in-kernel struct xdp_buff. * * This helper is similar to **bpf_perf_eventoutput**\ () but * restricted to raw_tracepoint bpf programs. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_get_netns_cookie(void *ctx) * Description * Retrieve the cookie (generated by the kernel) of the network * namespace the input *ctx* is associated with. The network * namespace cookie remains stable for its lifetime and provides * a global identifier that can be assumed unique. If *ctx* is * NULL, then the helper returns the cookie for the initial * network namespace. The cookie itself is very similar to that * of **bpf_get_socket_cookie**\ () helper, but for network * namespaces instead of sockets. * Return * A 8-byte long opaque number. * * u64 bpf_get_current_ancestor_cgroup_id(int ancestor_level) * Description * Return id of cgroup v2 that is ancestor of the cgroup associated * with the current task at the *ancestor_level*. The root cgroup * is at *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with the current task, then return value will be the * same as that of **bpf_get_current_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with the current task. * * The format of returned id and helper limitations are same as in * **bpf_get_current_cgroup_id**\ (). * Return * The id is returned or 0 in case the id could not be retrieved. * * long bpf_sk_assign(struct sk_buff *skb, void *sk, u64 flags) * Description * Helper is overloaded depending on BPF program type. This * description applies to **BPF_PROG_TYPE_SCHED_CLS** and * **BPF_PROG_TYPE_SCHED_ACT** programs. * * Assign the *sk* to the *skb*. When combined with appropriate * routing configuration to receive the packet towards the socket, * will cause *skb* to be delivered to the specified socket. * Subsequent redirection of *skb* via **bpf_redirect**\ (), * **bpf_clone_redirect**\ () or other methods outside of BPF may * interfere with successful delivery to the socket. * * This operation is only valid from TC ingress path. * * The *flags* argument must be zero. * Return * 0 on success, or a negative error in case of failure: * * **-EINVAL** if specified *flags* are not supported. * * **-ENOENT** if the socket is unavailable for assignment. * * **-ENETUNREACH** if the socket is unreachable (wrong netns). * * **-EOPNOTSUPP** if the operation is not supported, for example * a call from outside of TC ingress. * * long bpf_sk_assign(struct bpf_sk_lookup *ctx, struct bpf_sock *sk, u64 flags) * Description * Helper is overloaded depending on BPF program type. This * description applies to **BPF_PROG_TYPE_SK_LOOKUP** programs. * * Select the *sk* as a result of a socket lookup. * * For the operation to succeed passed socket must be compatible * with the packet description provided by the *ctx* object. * * L4 protocol (**IPPROTO_TCP** or **IPPROTO_UDP**) must * be an exact match. While IP family (**AF_INET** or * **AF_INET6**) must be compatible, that is IPv6 sockets * that are not v6-only can be selected for IPv4 packets. * * Only TCP listeners and UDP unconnected sockets can be * selected. *sk* can also be NULL to reset any previous * selection. * * *flags* argument can combination of following values: * * * **BPF_SK_LOOKUP_F_REPLACE** to override the previous * socket selection, potentially done by a BPF program * that ran before us. * * * **BPF_SK_LOOKUP_F_NO_REUSEPORT** to skip * load-balancing within reuseport group for the socket * being selected. * * On success *ctx->sk* will point to the selected socket. * * Return * 0 on success, or a negative errno in case of failure. * * * **-EAFNOSUPPORT** if socket family (*sk->family*) is * not compatible with packet family (*ctx->family*). * * * **-EEXIST** if socket has been already selected, * potentially by another program, and * **BPF_SK_LOOKUP_F_REPLACE** flag was not specified. * * * **-EINVAL** if unsupported flags were specified. * * * **-EPROTOTYPE** if socket L4 protocol * (*sk->protocol*) doesn't match packet protocol * (*ctx->protocol*). * * * **-ESOCKTNOSUPPORT** if socket is not in allowed * state (TCP listening or UDP unconnected). * * u64 bpf_ktime_get_boot_ns(void) * Description * Return the time elapsed since system boot, in nanoseconds. * Does include the time the system was suspended. * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) * Return * Current *ktime*. * * long bpf_seq_printf(struct seq_file *m, const char *fmt, u32 fmt_size, const void *data, u32 data_len) * Description * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print * out the format string. * The *m* represents the seq_file. The *fmt* and *fmt_size* are for * the format string itself. The *data* and *data_len* are format string * arguments. The *data* are a **u64** array and corresponding format string * values are stored in the array. For strings and pointers where pointees * are accessed, only the pointer values are stored in the *data* array. * The *data_len* is the size of *data* in bytes - must be a multiple of 8. * * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. * Reading kernel memory may fail due to either invalid address or * valid address but requiring a major memory fault. If reading kernel memory * fails, the string for **%s** will be an empty string, and the ip * address for **%p{i,I}{4,6}** will be 0. Not returning error to * bpf program is consistent with what **bpf_trace_printk**\ () does for now. * Return * 0 on success, or a negative error in case of failure: * * **-EBUSY** if per-CPU memory copy buffer is busy, can try again * by returning 1 from bpf program. * * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. * * **-E2BIG** if *fmt* contains too many format specifiers. * * **-EOVERFLOW** if an overflow happened: The same object will be tried again. * * long bpf_seq_write(struct seq_file *m, const void *data, u32 len) * Description * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. * The *m* represents the seq_file. The *data* and *len* represent the * data to write in bytes. * Return * 0 on success, or a negative error in case of failure: * * **-EOVERFLOW** if an overflow happened: The same object will be tried again. * * u64 bpf_sk_cgroup_id(void *sk) * Description * Return the cgroup v2 id of the socket *sk*. * * *sk* must be a non-**NULL** pointer to a socket, e.g. one * returned from **bpf_sk_lookup_xxx**\ (), * **bpf_sk_fullsock**\ (), etc. The format of returned id is * same as in **bpf_skb_cgroup_id**\ (). * * This helper is available only if the kernel was compiled with * the **CONFIG_SOCK_CGROUP_DATA** configuration option. * Return * The id is returned or 0 in case the id could not be retrieved. * * u64 bpf_sk_ancestor_cgroup_id(void *sk, int ancestor_level) * Description * Return id of cgroup v2 that is ancestor of cgroup associated * with the *sk* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *sk*, then return value will be same as that * of **bpf_sk_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *sk*. * * The format of returned id and helper limitations are same as in * **bpf_sk_cgroup_id**\ (). * Return * The id is returned or 0 in case the id could not be retrieved. * * long bpf_ringbuf_output(void *ringbuf, void *data, u64 size, u64 flags) * Description * Copy *size* bytes from *data* into a ring buffer *ringbuf*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * An adaptive notification is a notification sent whenever the user-space * process has caught up and consumed all available payloads. In case the user-space * process is still processing a previous payload, then no notification is needed * as it will process the newly added payload automatically. * Return * 0 on success, or a negative error in case of failure. * * void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags) * Description * Reserve *size* bytes of payload in a ring buffer *ringbuf*. * *flags* must be 0. * Return * Valid pointer with *size* bytes of memory available; NULL, * otherwise. * * void bpf_ringbuf_submit(void *data, u64 flags) * Description * Submit reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * Return * Nothing. Always succeeds. * * void bpf_ringbuf_discard(void *data, u64 flags) * Description * Discard reserved ring buffer sample, pointed to by *data*. * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. * If **0** is specified in *flags*, an adaptive notification * of new data availability is sent. * * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * Return * Nothing. Always succeeds. * * u64 bpf_ringbuf_query(void *ringbuf, u64 flags) * Description * Query various characteristics of provided ring buffer. What * exactly is queries is determined by *flags*: * * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. * * **BPF_RB_RING_SIZE**: The size of ring buffer. * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). * * Data returned is just a momentary snapshot of actual values * and could be inaccurate, so this facility should be used to * power heuristics and for reporting, not to make 100% correct * calculation. * Return * Requested value, or 0, if *flags* are not recognized. * * long bpf_csum_level(struct sk_buff *skb, u64 level) * Description * Change the skbs checksum level by one layer up or down, or * reset it entirely to none in order to have the stack perform * checksum validation. The level is applicable to the following * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | * through **bpf_skb_adjust_room**\ () helper with passing in * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since * the UDP header is removed. Similarly, an encap of the latter * into the former could be accompanied by a helper call to * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the * skb is still intended to be processed in higher layers of the * stack instead of just egressing at tc. * * There are three supported level settings at this time: * * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs * with CHECKSUM_UNNECESSARY. * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs * with CHECKSUM_UNNECESSARY. * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and * sets CHECKSUM_NONE to force checksum validation by the stack. * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current * skb->csum_level. * Return * 0 on success, or a negative error in case of failure. In the * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level * is returned or the error code -EACCES in case the skb is not * subject to CHECKSUM_UNNECESSARY. * * struct tcp6_sock *bpf_skc_to_tcp6_sock(void *sk) * Description * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. * Return * *sk* if casting is valid, or **NULL** otherwise. * * struct tcp_sock *bpf_skc_to_tcp_sock(void *sk) * Description * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. * Return * *sk* if casting is valid, or **NULL** otherwise. * * struct tcp_timewait_sock *bpf_skc_to_tcp_timewait_sock(void *sk) * Description * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. * Return * *sk* if casting is valid, or **NULL** otherwise. * * struct tcp_request_sock *bpf_skc_to_tcp_request_sock(void *sk) * Description * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. * Return * *sk* if casting is valid, or **NULL** otherwise. * * struct udp6_sock *bpf_skc_to_udp6_sock(void *sk) * Description * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. * Return * *sk* if casting is valid, or **NULL** otherwise. * * long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags) * Description * Return a user or a kernel stack in bpf program provided buffer. * Note: the user stack will only be populated if the *task* is * the current task; all other tasks will return -EOPNOTSUPP. * To achieve this, the helper needs *task*, which is a valid * pointer to **struct task_struct**. To store the stacktrace, the * bpf program provides *buf* with a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * The *task* must be the current task. * **BPF_F_USER_BUILD_ID** * Collect buildid+offset instead of ips for user stack, * only valid if **BPF_F_USER_STACK** is also specified. * * **bpf_get_task_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * Return * The non-negative copied *buf* length equal to or less than * *size* on success, or a negative error in case of failure. * * long bpf_load_hdr_opt(struct bpf_sock_ops *skops, void *searchby_res, u32 len, u64 flags) * Description * Load header option. Support reading a particular TCP header * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). * * If *flags* is 0, it will search the option from the * *skops*\ **->skb_data**. The comment in **struct bpf_sock_ops** * has details on what skb_data contains under different * *skops*\ **->op**. * * The first byte of the *searchby_res* specifies the * kind that it wants to search. * * If the searching kind is an experimental kind * (i.e. 253 or 254 according to RFC6994). It also * needs to specify the "magic" which is either * 2 bytes or 4 bytes. It then also needs to * specify the size of the magic by using * the 2nd byte which is "kind-length" of a TCP * header option and the "kind-length" also * includes the first 2 bytes "kind" and "kind-length" * itself as a normal TCP header option also does. * * For example, to search experimental kind 254 with * 2 byte magic 0xeB9F, the searchby_res should be * [ 254, 4, 0xeB, 0x9F, 0, 0, .... 0 ]. * * To search for the standard window scale option (3), * the *searchby_res* should be [ 3, 0, 0, .... 0 ]. * Note, kind-length must be 0 for regular option. * * Searching for No-Op (0) and End-of-Option-List (1) are * not supported. * * *len* must be at least 2 bytes which is the minimal size * of a header option. * * Supported flags: * * * **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the * saved_syn packet or the just-received syn packet. * * Return * > 0 when found, the header option is copied to *searchby_res*. * The return value is the total length copied. On failure, a * negative error code is returned: * * **-EINVAL** if a parameter is invalid. * * **-ENOMSG** if the option is not found. * * **-ENOENT** if no syn packet is available when * **BPF_LOAD_HDR_OPT_TCP_SYN** is used. * * **-ENOSPC** if there is not enough space. Only *len* number of * bytes are copied. * * **-EFAULT** on failure to parse the header options in the * packet. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. * * long bpf_store_hdr_opt(struct bpf_sock_ops *skops, const void *from, u32 len, u64 flags) * Description * Store header option. The data will be copied * from buffer *from* with length *len* to the TCP header. * * The buffer *from* should have the whole option that * includes the kind, kind-length, and the actual * option data. The *len* must be at least kind-length * long. The kind-length does not have to be 4 byte * aligned. The kernel will take care of the padding * and setting the 4 bytes aligned value to th->doff. * * This helper will check for duplicated option * by searching the same option in the outgoing skb. * * This helper can only be called during * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. * * Return * 0 on success, or negative error in case of failure: * * **-EINVAL** If param is invalid. * * **-ENOSPC** if there is not enough space in the header. * Nothing has been written * * **-EEXIST** if the option already exists. * * **-EFAULT** on failure to parse the existing header options. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. * * long bpf_reserve_hdr_opt(struct bpf_sock_ops *skops, u32 len, u64 flags) * Description * Reserve *len* bytes for the bpf header option. The * space will be used by **bpf_store_hdr_opt**\ () later in * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. * * If **bpf_reserve_hdr_opt**\ () is called multiple times, * the total number of bytes will be reserved. * * This helper can only be called during * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**. * * Return * 0 on success, or negative error in case of failure: * * **-EINVAL** if a parameter is invalid. * * **-ENOSPC** if there is not enough space in the header. * * **-EPERM** if the helper cannot be used under the current * *skops*\ **->op**. * * void *bpf_inode_storage_get(struct bpf_map *map, void *inode, void *value, u64 flags) * Description * Get a bpf_local_storage from an *inode*. * * Logically, it could be thought of as getting the value from * a *map* with *inode* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *inode*) except this * helper enforces the key must be an inode and the map must also * be a **BPF_MAP_TYPE_INODE_STORAGE**. * * Underneath, the value is stored locally at *inode* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf_local_storage residing at *inode*. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * Return * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. * * int bpf_inode_storage_delete(struct bpf_map *map, void *inode) * Description * Delete a bpf_local_storage from an *inode*. * Return * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. * * long bpf_d_path(struct path *path, char *buf, u32 sz) * Description * Return full path for given **struct path** object, which * needs to be the kernel BTF *path* object. The path is * returned in the provided buffer *buf* of size *sz* and * is zero terminated. * * Return * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. * * long bpf_copy_from_user(void *dst, u32 size, const void *user_ptr) * Description * Read *size* bytes from user space address *user_ptr* and store * the data in *dst*. This is a wrapper of **copy_from_user**\ (). * Return * 0 on success, or a negative error in case of failure. * * long bpf_snprintf_btf(char *str, u32 str_size, struct btf_ptr *ptr, u32 btf_ptr_size, u64 flags) * Description * Use BTF to store a string representation of *ptr*->ptr in *str*, * using *ptr*->type_id. This value should specify the type * that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1) * can be used to look up vmlinux BTF type ids. Traversing the * data structure using BTF, the type information and values are * stored in the first *str_size* - 1 bytes of *str*. Safe copy of * the pointer data is carried out to avoid kernel crashes during * operation. Smaller types can use string space on the stack; * larger programs can use map data to store the string * representation. * * The string can be subsequently shared with userspace via * bpf_perf_event_output() or ring buffer interfaces. * bpf_trace_printk() is to be avoided as it places too small * a limit on string size to be useful. * * *flags* is a combination of * * **BTF_F_COMPACT** * no formatting around type information * **BTF_F_NONAME** * no struct/union member names/types * **BTF_F_PTR_RAW** * show raw (unobfuscated) pointer values; * equivalent to printk specifier %px. * **BTF_F_ZERO** * show zero-valued struct/union members; they * are not displayed by default * * Return * The number of bytes that were written (or would have been * written if output had to be truncated due to string size), * or a negative error in cases of failure. * * long bpf_seq_printf_btf(struct seq_file *m, struct btf_ptr *ptr, u32 ptr_size, u64 flags) * Description * Use BTF to write to seq_write a string representation of * *ptr*->ptr, using *ptr*->type_id as per bpf_snprintf_btf(). * *flags* are identical to those used for bpf_snprintf_btf. * Return * 0 on success or a negative error in case of failure. * * u64 bpf_skb_cgroup_classid(struct sk_buff *skb) * Description * See **bpf_get_cgroup_classid**\ () for the main description. * This helper differs from **bpf_get_cgroup_classid**\ () in that * the cgroup v1 net_cls class is retrieved only from the *skb*'s * associated socket instead of the current process. * Return * The id is returned or 0 in case the id could not be retrieved. * * long bpf_redirect_neigh(u32 ifindex, struct bpf_redir_neigh *params, int plen, u64 flags) * Description * Redirect the packet to another net device of index *ifindex* * and fill in L2 addresses from neighboring subsystem. This helper * is somewhat similar to **bpf_redirect**\ (), except that it * populates L2 addresses as well, meaning, internally, the helper * relies on the neighbor lookup for the L2 address of the nexthop. * * The helper will perform a FIB lookup based on the skb's * networking header to get the address of the next hop, unless * this is supplied by the caller in the *params* argument. The * *plen* argument indicates the len of *params* and should be set * to 0 if *params* is NULL. * * The *flags* argument is reserved and must be 0. The helper is * currently only supported for tc BPF program types, and enabled * for IPv4 and IPv6 protocols. * Return * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. * * void *bpf_per_cpu_ptr(const void *percpu_ptr, u32 cpu) * Description * Take a pointer to a percpu ksym, *percpu_ptr*, and return a * pointer to the percpu kernel variable on *cpu*. A ksym is an * extern variable decorated with '__ksym'. For ksym, there is a * global var (either static or global) defined of the same name * in the kernel. The ksym is percpu if the global var is percpu. * The returned pointer points to the global percpu var on *cpu*. * * bpf_per_cpu_ptr() has the same semantic as per_cpu_ptr() in the * kernel, except that bpf_per_cpu_ptr() may return NULL. This * happens if *cpu* is larger than nr_cpu_ids. The caller of * bpf_per_cpu_ptr() must check the returned value. * Return * A pointer pointing to the kernel percpu variable on *cpu*, or * NULL, if *cpu* is invalid. * * void *bpf_this_cpu_ptr(const void *percpu_ptr) * Description * Take a pointer to a percpu ksym, *percpu_ptr*, and return a * pointer to the percpu kernel variable on this cpu. See the * description of 'ksym' in **bpf_per_cpu_ptr**\ (). * * bpf_this_cpu_ptr() has the same semantic as this_cpu_ptr() in * the kernel. Different from **bpf_per_cpu_ptr**\ (), it would * never return NULL. * Return * A pointer pointing to the kernel percpu variable on this cpu. * * long bpf_redirect_peer(u32 ifindex, u64 flags) * Description * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_redirect**\ (), except * that the redirection happens to the *ifindex*' peer device and * the netns switch takes place from ingress to ingress without * going through the CPU's backlog queue. * * *skb*\ **->mark** and *skb*\ **->tstamp** are not cleared during * the netns switch. * * The *flags* argument is reserved and must be 0. The helper is * currently only supported for tc BPF program types at the * ingress hook and for veth and netkit target device types. The * peer device must reside in a different network namespace. * Return * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. * * void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, void *value, u64 flags) * Description * Get a bpf_local_storage from the *task*. * * Logically, it could be thought of as getting the value from * a *map* with *task* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this * helper enforces the key must be a task_struct and the map must also * be a **BPF_MAP_TYPE_TASK_STORAGE**. * * Underneath, the value is stored locally at *task* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf_local_storage residing at *task*. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * Return * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. * * long bpf_task_storage_delete(struct bpf_map *map, struct task_struct *task) * Description * Delete a bpf_local_storage from a *task*. * Return * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. * * struct task_struct *bpf_get_current_task_btf(void) * Description * Return a BTF pointer to the "current" task. * This pointer can also be used in helpers that accept an * *ARG_PTR_TO_BTF_ID* of type *task_struct*. * Return * Pointer to the current task. * * long bpf_bprm_opts_set(struct linux_binprm *bprm, u64 flags) * Description * Set or clear certain options on *bprm*: * * **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit * which sets the **AT_SECURE** auxv for glibc. The bit * is cleared if the flag is not specified. * Return * **-EINVAL** if invalid *flags* are passed, zero otherwise. * * u64 bpf_ktime_get_coarse_ns(void) * Description * Return a coarse-grained version of the time elapsed since * system boot, in nanoseconds. Does not include time the system * was suspended. * * See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**) * Return * Current *ktime*. * * long bpf_ima_inode_hash(struct inode *inode, void *dst, u32 size) * Description * Returns the stored IMA hash of the *inode* (if it's available). * If the hash is larger than *size*, then only *size* * bytes will be copied to *dst* * Return * The **hash_algo** is returned on success, * **-EOPNOTSUPP** if IMA is disabled or **-EINVAL** if * invalid arguments are passed. * * struct socket *bpf_sock_from_file(struct file *file) * Description * If the given file represents a socket, returns the associated * socket. * Return * A pointer to a struct socket on success or NULL if the file is * not a socket. * * long bpf_check_mtu(void *ctx, u32 ifindex, u32 *mtu_len, s32 len_diff, u64 flags) * Description * Check packet size against exceeding MTU of net device (based * on *ifindex*). This helper will likely be used in combination * with helpers that adjust/change the packet size. * * The argument *len_diff* can be used for querying with a planned * size change. This allows to check MTU prior to changing packet * ctx. Providing a *len_diff* adjustment that is larger than the * actual packet size (resulting in negative packet size) will in * principle not exceed the MTU, which is why it is not considered * a failure. Other BPF helpers are needed for performing the * planned size change; therefore the responsibility for catching * a negative packet size belongs in those helpers. * * Specifying *ifindex* zero means the MTU check is performed * against the current net device. This is practical if this isn't * used prior to redirect. * * On input *mtu_len* must be a valid pointer, else verifier will * reject BPF program. If the value *mtu_len* is initialized to * zero then the ctx packet size is use. When value *mtu_len* is * provided as input this specify the L3 length that the MTU check * is done against. Remember XDP and TC length operate at L2, but * this value is L3 as this correlate to MTU and IP-header tot_len * values which are L3 (similar behavior as bpf_fib_lookup). * * The Linux kernel route table can configure MTUs on a more * specific per route level, which is not provided by this helper. * For route level MTU checks use the **bpf_fib_lookup**\ () * helper. * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** for tc cls_act programs. * * The *flags* argument can be a combination of one or more of the * following values: * * **BPF_MTU_CHK_SEGS** * This flag will only works for *ctx* **struct sk_buff**. * If packet context contains extra packet segment buffers * (often knows as GSO skb), then MTU check is harder to * check at this point, because in transmit path it is * possible for the skb packet to get re-segmented * (depending on net device features). This could still be * a MTU violation, so this flag enables performing MTU * check against segments, with a different violation * return code to tell it apart. Check cannot use len_diff. * * On return *mtu_len* pointer contains the MTU value of the net * device. Remember the net device configured MTU is the L3 size, * which is returned here and XDP and TC length operate at L2. * Helper take this into account for you, but remember when using * MTU value in your BPF-code. * * Return * * 0 on success, and populate MTU value in *mtu_len* pointer. * * * < 0 if any input argument is invalid (*mtu_len* not updated) * * MTU violations return positive values, but also populate MTU * value in *mtu_len* pointer, as this can be needed for * implementing PMTU handing: * * * **BPF_MTU_CHK_RET_FRAG_NEEDED** * * **BPF_MTU_CHK_RET_SEGS_TOOBIG** * * long bpf_for_each_map_elem(struct bpf_map *map, void *callback_fn, void *callback_ctx, u64 flags) * Description * For each element in **map**, call **callback_fn** function with * **map**, **callback_ctx** and other map-specific parameters. * The **callback_fn** should be a static function and * the **callback_ctx** should be a pointer to the stack. * The **flags** is used to control certain aspects of the helper. * Currently, the **flags** must be 0. * * The following are a list of supported map types and their * respective expected callback signatures: * * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH, * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY * * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx); * * For per_cpu maps, the map_value is the value on the cpu where the * bpf_prog is running. * * If **callback_fn** return 0, the helper will continue to the next * element. If return value is 1, the helper will skip the rest of * elements and return. Other return values are not used now. * * Return * The number of traversed map elements for success, **-EINVAL** for * invalid **flags**. * * long bpf_snprintf(char *str, u32 str_size, const char *fmt, u64 *data, u32 data_len) * Description * Outputs a string into the **str** buffer of size **str_size** * based on a format string stored in a read-only map pointed by * **fmt**. * * Each format specifier in **fmt** corresponds to one u64 element * in the **data** array. For strings and pointers where pointees * are accessed, only the pointer values are stored in the *data* * array. The *data_len* is the size of *data* in bytes - must be * a multiple of 8. * * Formats **%s** and **%p{i,I}{4,6}** require to read kernel * memory. Reading kernel memory may fail due to either invalid * address or valid address but requiring a major memory fault. If * reading kernel memory fails, the string for **%s** will be an * empty string, and the ip address for **%p{i,I}{4,6}** will be 0. * Not returning error to bpf program is consistent with what * **bpf_trace_printk**\ () does for now. * * Return * The strictly positive length of the formatted string, including * the trailing zero character. If the return value is greater than * **str_size**, **str** contains a truncated string, guaranteed to * be zero-terminated except when **str_size** is 0. * * Or **-EBUSY** if the per-CPU memory copy buffer is busy. * * long bpf_sys_bpf(u32 cmd, void *attr, u32 attr_size) * Description * Execute bpf syscall with given arguments. * Return * A syscall result. * * long bpf_btf_find_by_name_kind(char *name, int name_sz, u32 kind, int flags) * Description * Find BTF type with given name and kind in vmlinux BTF or in module's BTFs. * Return * Returns btf_id and btf_obj_fd in lower and upper 32 bits. * * long bpf_sys_close(u32 fd) * Description * Execute close syscall for given FD. * Return * A syscall result. * * long bpf_timer_init(struct bpf_timer *timer, struct bpf_map *map, u64 flags) * Description * Initialize the timer. * First 4 bits of *flags* specify clockid. * Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed. * All other bits of *flags* are reserved. * The verifier will reject the program if *timer* is not from * the same *map*. * Return * 0 on success. * **-EBUSY** if *timer* is already initialized. * **-EINVAL** if invalid *flags* are passed. * **-EPERM** if *timer* is in a map that doesn't have any user references. * The user space should either hold a file descriptor to a map with timers * or pin such map in bpffs. When map is unpinned or file descriptor is * closed all timers in the map will be cancelled and freed. * * long bpf_timer_set_callback(struct bpf_timer *timer, void *callback_fn) * Description * Configure the timer to call *callback_fn* static function. * Return * 0 on success. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. * **-EPERM** if *timer* is in a map that doesn't have any user references. * The user space should either hold a file descriptor to a map with timers * or pin such map in bpffs. When map is unpinned or file descriptor is * closed all timers in the map will be cancelled and freed. * * long bpf_timer_start(struct bpf_timer *timer, u64 nsecs, u64 flags) * Description * Set timer expiration N nanoseconds from the current time. The * configured callback will be invoked in soft irq context on some cpu * and will not repeat unless another bpf_timer_start() is made. * In such case the next invocation can migrate to a different cpu. * Since struct bpf_timer is a field inside map element the map * owns the timer. The bpf_timer_set_callback() will increment refcnt * of BPF program to make sure that callback_fn code stays valid. * When user space reference to a map reaches zero all timers * in a map are cancelled and corresponding program's refcnts are * decremented. This is done to make sure that Ctrl-C of a user * process doesn't leave any timers running. If map is pinned in * bpffs the callback_fn can re-arm itself indefinitely. * bpf_map_update/delete_elem() helpers and user space sys_bpf commands * cancel and free the timer in the given map element. * The map can contain timers that invoke callback_fn-s from different * programs. The same callback_fn can serve different timers from * different maps if key/value layout matches across maps. * Every bpf_timer_set_callback() can have different callback_fn. * * *flags* can be one of: * * **BPF_F_TIMER_ABS** * Start the timer in absolute expire value instead of the * default relative one. * **BPF_F_TIMER_CPU_PIN** * Timer will be pinned to the CPU of the caller. * * Return * 0 on success. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier * or invalid *flags* are passed. * * long bpf_timer_cancel(struct bpf_timer *timer) * Description * Cancel the timer and wait for callback_fn to finish if it was running. * Return * 0 if the timer was not active. * 1 if the timer was active. * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. * **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its * own timer which would have led to a deadlock otherwise. * * u64 bpf_get_func_ip(void *ctx) * Description * Get address of the traced function (for tracing and kprobe programs). * * When called for kprobe program attached as uprobe it returns * probe address for both entry and return uprobe. * * 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. * * u64 bpf_get_attach_cookie(void *ctx) * Description * Get bpf_cookie value provided (optionally) during the program * attachment. It might be different for each individual * attachment, even if BPF program itself is the same. * Expects BPF program context *ctx* as a first argument. * * Supported for the following program types: * - kprobe/uprobe; * - tracepoint; * - perf_event. * Return * Value specified by user at BPF link creation/attachment time * or 0, if it was not specified. * * long bpf_task_pt_regs(struct task_struct *task) * Description * Get the struct pt_regs associated with **task**. * Return * A pointer to struct pt_regs. * * long bpf_get_branch_snapshot(void *entries, u32 size, u64 flags) * Description * Get branch trace from hardware engines like Intel LBR. The * hardware engine is stopped shortly after the helper is * called. Therefore, the user need to filter branch entries * based on the actual use case. To capture branch trace * before the trigger point of the BPF program, the helper * should be called at the beginning of the BPF program. * * The data is stored as struct perf_branch_entry into output * buffer *entries*. *size* is the size of *entries* in bytes. * *flags* is reserved for now and must be zero. * * Return * On success, number of bytes written to *buf*. On error, a * negative value. * * **-EINVAL** if *flags* is not zero. * * **-ENOENT** if architecture does not support branch records. * * long bpf_trace_vprintk(const char *fmt, u32 fmt_size, const void *data, u32 data_len) * Description * Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64 * to format and can handle more format args as a result. * * Arguments are to be used as in **bpf_seq_printf**\ () helper. * Return * The number of bytes written to the buffer, or a negative error * in case of failure. * * struct unix_sock *bpf_skc_to_unix_sock(void *sk) * Description * Dynamically cast a *sk* pointer to a *unix_sock* pointer. * Return * *sk* if casting is valid, or **NULL** otherwise. * * long bpf_kallsyms_lookup_name(const char *name, int name_sz, int flags, u64 *res) * Description * Get the address of a kernel symbol, returned in *res*. *res* is * set to 0 if the symbol is not found. * Return * On success, zero. On error, a negative value. * * **-EINVAL** if *flags* is not zero. * * **-EINVAL** if string *name* is not the same size as *name_sz*. * * **-ENOENT** if symbol is not found. * * **-EPERM** if caller does not have permission to obtain kernel address. * * long bpf_find_vma(struct task_struct *task, u64 addr, void *callback_fn, void *callback_ctx, u64 flags) * Description * Find vma of *task* that contains *addr*, call *callback_fn* * function with *task*, *vma*, and *callback_ctx*. * The *callback_fn* should be a static function and * the *callback_ctx* should be a pointer to the stack. * The *flags* is used to control certain aspects of the helper. * Currently, the *flags* must be 0. * * The expected callback signature is * * long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx); * * Return * 0 on success. * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. * **-EBUSY** if failed to try lock mmap_lock. * **-EINVAL** for invalid **flags**. * * long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags) * Description * For **nr_loops**, call **callback_fn** function * with **callback_ctx** as the context parameter. * The **callback_fn** should be a static function and * the **callback_ctx** should be a pointer to the stack. * The **flags** is used to control certain aspects of the helper. * Currently, the **flags** must be 0. Currently, nr_loops is * limited to 1 << 23 (~8 million) loops. * * long (\*callback_fn)(u64 index, void \*ctx); * * where **index** is the current index in the loop. The index * is zero-indexed. * * If **callback_fn** returns 0, the helper will continue to the next * loop. If return value is 1, the helper will skip the rest of * the loops and return. Other return values are not used now, * and will be rejected by the verifier. * * Return * The number of loops performed, **-EINVAL** for invalid **flags**, * **-E2BIG** if **nr_loops** exceeds the maximum number of loops. * * long bpf_strncmp(const char *s1, u32 s1_sz, const char *s2) * Description * Do strncmp() between **s1** and **s2**. **s1** doesn't need * to be null-terminated and **s1_sz** is the maximum storage * size of **s1**. **s2** must be a read-only string. * Return * An integer less than, equal to, or greater than zero * if the first **s1_sz** bytes of **s1** is found to be * less than, to match, or be greater than **s2**. * * long bpf_get_func_arg(void *ctx, u32 n, u64 *value) * Description * Get **n**-th argument register (zero based) of the traced function (for tracing programs) * returned in **value**. * * Return * 0 on success. * **-EINVAL** if n >= argument register count of traced function. * * long bpf_get_func_ret(void *ctx, u64 *value) * Description * Get return value of the traced function (for tracing programs) * in **value**. * * Return * 0 on success. * **-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN. * * long bpf_get_func_arg_cnt(void *ctx) * Description * Get number of registers of the traced function (for tracing programs) where * function arguments are stored in these registers. * * Return * The number of argument registers of the traced function. * * int bpf_get_retval(void) * Description * Get the BPF program's return value that will be returned to the upper layers. * * This helper is currently supported by cgroup programs and only by the hooks * where BPF program's return value is returned to the userspace via errno. * Return * The BPF program's return value. * * int bpf_set_retval(int retval) * Description * Set the BPF program's return value that will be returned to the upper layers. * * This helper is currently supported by cgroup programs and only by the hooks * where BPF program's return value is returned to the userspace via errno. * * Note that there is the following corner case where the program exports an error * via bpf_set_retval but signals success via 'return 1': * * bpf_set_retval(-EPERM); * return 1; * * In this case, the BPF program's return value will use helper's -EPERM. This * still holds true for cgroup/bind{4,6} which supports extra 'return 3' success case. * * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_xdp_get_buff_len(struct xdp_buff *xdp_md) * Description * Get the total size of a given xdp buff (linear and paged area) * Return * The total size of a given xdp buffer. * * long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len) * Description * This helper is provided as an easy way to load data from a * xdp buffer. It can be used to load *len* bytes from *offset* from * the frame associated to *xdp_md*, into the buffer pointed by * *buf*. * Return * 0 on success, or a negative error in case of failure. * * long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len) * Description * Store *len* bytes from buffer *buf* into the frame * associated to *xdp_md*, at *offset*. * Return * 0 on success, or a negative error in case of failure. * * long bpf_copy_from_user_task(void *dst, u32 size, const void *user_ptr, struct task_struct *tsk, u64 flags) * Description * Read *size* bytes from user space address *user_ptr* in *tsk*'s * address space, and stores the data in *dst*. *flags* is not * used yet and is provided for future extensibility. This helper * can only be used by sleepable programs. * Return * 0 on success, or a negative error in case of failure. On error * *dst* buffer is zeroed out. * * long bpf_skb_set_tstamp(struct sk_buff *skb, u64 tstamp, u32 tstamp_type) * Description * Change the __sk_buff->tstamp_type to *tstamp_type* * and set *tstamp* to the __sk_buff->tstamp together. * * If there is no need to change the __sk_buff->tstamp_type, * the tstamp value can be directly written to __sk_buff->tstamp * instead. * * BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that * will be kept during bpf_redirect_*(). A non zero * *tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO * *tstamp_type*. * * A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used * with a zero *tstamp*. * * Only IPv4 and IPv6 skb->protocol are supported. * * This function is most useful when it needs to set a * mono delivery time to __sk_buff->tstamp and then * bpf_redirect_*() to the egress of an iface. For example, * changing the (rcv) timestamp in __sk_buff->tstamp at * ingress to a mono delivery time and then bpf_redirect_*() * to sch_fq@phy-dev. * Return * 0 on success. * **-EINVAL** for invalid input * **-EOPNOTSUPP** for unsupported protocol * * long bpf_ima_file_hash(struct file *file, void *dst, u32 size) * Description * Returns a calculated IMA hash of the *file*. * If the hash is larger than *size*, then only *size* * bytes will be copied to *dst* * Return * The **hash_algo** is returned on success, * **-EOPNOTSUPP** if the hash calculation failed or **-EINVAL** if * invalid arguments are passed. * * void *bpf_kptr_xchg(void *dst, void *ptr) * Description * Exchange kptr at pointer *dst* with *ptr*, and return the old value. * *dst* can be map value or local kptr. *ptr* can be NULL, otherwise * it must be a referenced pointer which will be released when this helper * is called. * Return * The old value of kptr (which can be NULL). The returned pointer * if not NULL, is a reference which must be released using its * corresponding release function, or moved into a BPF map before * program exit. * * void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu) * Description * Perform a lookup in *percpu map* for an entry associated to * *key* on *cpu*. * Return * Map value associated to *key* on *cpu*, or **NULL** if no entry * was found or *cpu* is invalid. * * struct mptcp_sock *bpf_skc_to_mptcp_sock(void *sk) * Description * Dynamically cast a *sk* pointer to a *mptcp_sock* pointer. * Return * *sk* if casting is valid, or **NULL** otherwise. * * long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr) * Description * Get a dynptr to local memory *data*. * * *data* must be a ptr to a map value. * The maximum *size* supported is DYNPTR_MAX_SIZE. * *flags* is currently unused. * Return * 0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE, * -EINVAL if flags is not 0. * * long bpf_ringbuf_reserve_dynptr(void *ringbuf, u32 size, u64 flags, struct bpf_dynptr *ptr) * Description * Reserve *size* bytes of payload in a ring buffer *ringbuf* * through the dynptr interface. *flags* must be 0. * * Please note that a corresponding bpf_ringbuf_submit_dynptr or * bpf_ringbuf_discard_dynptr must be called on *ptr*, even if the * reservation fails. This is enforced by the verifier. * Return * 0 on success, or a negative error in case of failure. * * void bpf_ringbuf_submit_dynptr(struct bpf_dynptr *ptr, u64 flags) * Description * Submit reserved ring buffer sample, pointed to by *data*, * through the dynptr interface. This is a no-op if the dynptr is * invalid/null. * * For more information on *flags*, please see * 'bpf_ringbuf_submit'. * Return * Nothing. Always succeeds. * * void bpf_ringbuf_discard_dynptr(struct bpf_dynptr *ptr, u64 flags) * Description * Discard reserved ring buffer sample through the dynptr * interface. This is a no-op if the dynptr is invalid/null. * * For more information on *flags*, please see * 'bpf_ringbuf_discard'. * Return * Nothing. Always succeeds. * * long bpf_dynptr_read(void *dst, u32 len, const struct bpf_dynptr *src, u32 offset, u64 flags) * Description * Read *len* bytes from *src* into *dst*, starting from *offset* * into *src*. * *flags* is currently unused. * Return * 0 on success, -E2BIG if *offset* + *len* exceeds the length * of *src*'s data, -EINVAL if *src* is an invalid dynptr or if * *flags* is not 0. * * long bpf_dynptr_write(const struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags) * Description * Write *len* bytes from *src* into *dst*, starting from *offset* * into *dst*. * * *flags* must be 0 except for skb-type dynptrs. * * For skb-type dynptrs: * * All data slices of the dynptr are automatically * invalidated after **bpf_dynptr_write**\ (). This is * because writing may pull the skb and change the * underlying packet buffer. * * * For *flags*, please see the flags accepted by * **bpf_skb_store_bytes**\ (). * Return * 0 on success, -E2BIG if *offset* + *len* exceeds the length * of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst* * is a read-only dynptr or if *flags* is not correct. For skb-type dynptrs, * other errors correspond to errors returned by **bpf_skb_store_bytes**\ (). * * void *bpf_dynptr_data(const struct bpf_dynptr *ptr, u32 offset, u32 len) * Description * Get a pointer to the underlying dynptr data. * * *len* must be a statically known value. The returned data slice * is invalidated whenever the dynptr is invalidated. * * skb and xdp type dynptrs may not use bpf_dynptr_data. They should * instead use bpf_dynptr_slice and bpf_dynptr_slice_rdwr. * Return * Pointer to the underlying dynptr data, NULL if the dynptr is * read-only, if the dynptr is invalid, or if the offset and length * is out of bounds. * * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len) * Description * Try to issue a SYN cookie for the packet with corresponding * IPv4/TCP headers, *iph* and *th*, without depending on a * listening socket. * * *iph* points to the IPv4 header. * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * Return * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** if *th_len* is invalid. * * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len) * Description * Try to issue a SYN cookie for the packet with corresponding * IPv6/TCP headers, *iph* and *th*, without depending on a * listening socket. * * *iph* points to the IPv6 header. * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header (at least * **sizeof**\ (**struct tcphdr**)). * Return * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** if *th_len* is invalid. * * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. * * long bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th) * Description * Check whether *iph* and *th* contain a valid SYN cookie ACK * without depending on a listening socket. * * *iph* points to the IPv4 header. * * *th* points to the TCP header. * Return * 0 if *iph* and *th* are a valid SYN cookie ACK. * * On failure, the returned value is one of the following: * * **-EACCES** if the SYN cookie is not valid. * * long bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th) * Description * Check whether *iph* and *th* contain a valid SYN cookie ACK * without depending on a listening socket. * * *iph* points to the IPv6 header. * * *th* points to the TCP header. * Return * 0 if *iph* and *th* are a valid SYN cookie ACK. * * On failure, the returned value is one of the following: * * **-EACCES** if the SYN cookie is not valid. * * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. * * u64 bpf_ktime_get_tai_ns(void) * Description * A nonsettable system-wide clock derived from wall-clock time but * ignoring leap seconds. This clock does not experience * discontinuities and backwards jumps caused by NTP inserting leap * seconds as CLOCK_REALTIME does. * * See: **clock_gettime**\ (**CLOCK_TAI**) * Return * Current *ktime*. * * long bpf_user_ringbuf_drain(struct bpf_map *map, void *callback_fn, void *ctx, u64 flags) * Description * Drain samples from the specified user ring buffer, and invoke * the provided callback for each such sample: * * long (\*callback_fn)(const struct bpf_dynptr \*dynptr, void \*ctx); * * If **callback_fn** returns 0, the helper will continue to try * and drain the next sample, up to a maximum of * BPF_MAX_USER_RINGBUF_SAMPLES samples. If the return value is 1, * the helper will skip the rest of the samples and return. Other * return values are not used now, and will be rejected by the * verifier. * Return * The number of drained samples if no error was encountered while * draining samples, or 0 if no samples were present in the ring * buffer. If a user-space producer was epoll-waiting on this map, * and at least one sample was drained, they will receive an event * notification notifying them of available space in the ring * buffer. If the BPF_RB_NO_WAKEUP flag is passed to this * function, no wakeup notification will be sent. If the * BPF_RB_FORCE_WAKEUP flag is passed, a wakeup notification will * be sent even if no sample was drained. * * On failure, the returned value is one of the following: * * **-EBUSY** if the ring buffer is contended, and another calling * context was concurrently draining the ring buffer. * * **-EINVAL** if user-space is not properly tracking the ring * buffer due to the producer position not being aligned to 8 * bytes, a sample not being aligned to 8 bytes, or the producer * position not matching the advertised length of a sample. * * **-E2BIG** if user-space has tried to publish a sample which is * larger than the size of the ring buffer, or which cannot fit * within a struct bpf_dynptr. * * void *bpf_cgrp_storage_get(struct bpf_map *map, struct cgroup *cgroup, void *value, u64 flags) * Description * Get a bpf_local_storage from the *cgroup*. * * Logically, it could be thought of as getting the value from * a *map* with *cgroup* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *cgroup*) except this * helper enforces the key must be a cgroup struct and the map must also * be a **BPF_MAP_TYPE_CGRP_STORAGE**. * * In reality, the local-storage value is embedded directly inside of the * *cgroup* object itself, rather than being located in the * **BPF_MAP_TYPE_CGRP_STORAGE** map. When the local-storage value is * queried for some *map* on a *cgroup* object, the kernel will perform an * O(n) iteration over all of the live local-storage values for that * *cgroup* object until the local-storage value for the *map* is found. * * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be * used such that a new bpf_local_storage will be * created if one does not exist. *value* can be used * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf_local_storage. If *value* is * **NULL**, the new bpf_local_storage will be zero initialized. * Return * A bpf_local_storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf_local_storage. * * long bpf_cgrp_storage_delete(struct bpf_map *map, struct cgroup *cgroup) * Description * Delete a bpf_local_storage from a *cgroup*. * Return * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. */ #define ___BPF_FUNC_MAPPER(FN, ctx...) \ FN(unspec, 0, ##ctx) \ FN(map_lookup_elem, 1, ##ctx) \ FN(map_update_elem, 2, ##ctx) \ FN(map_delete_elem, 3, ##ctx) \ FN(probe_read, 4, ##ctx) \ FN(ktime_get_ns, 5, ##ctx) \ FN(trace_printk, 6, ##ctx) \ FN(get_prandom_u32, 7, ##ctx) \ FN(get_smp_processor_id, 8, ##ctx) \ FN(skb_store_bytes, 9, ##ctx) \ FN(l3_csum_replace, 10, ##ctx) \ FN(l4_csum_replace, 11, ##ctx) \ FN(tail_call, 12, ##ctx) \ FN(clone_redirect, 13, ##ctx) \ FN(get_current_pid_tgid, 14, ##ctx) \ FN(get_current_uid_gid, 15, ##ctx) \ FN(get_current_comm, 16, ##ctx) \ FN(get_cgroup_classid, 17, ##ctx) \ FN(skb_vlan_push, 18, ##ctx) \ FN(skb_vlan_pop, 19, ##ctx) \ FN(skb_get_tunnel_key, 20, ##ctx) \ FN(skb_set_tunnel_key, 21, ##ctx) \ FN(perf_event_read, 22, ##ctx) \ FN(redirect, 23, ##ctx) \ FN(get_route_realm, 24, ##ctx) \ FN(perf_event_output, 25, ##ctx) \ FN(skb_load_bytes, 26, ##ctx) \ FN(get_stackid, 27, ##ctx) \ FN(csum_diff, 28, ##ctx) \ FN(skb_get_tunnel_opt, 29, ##ctx) \ FN(skb_set_tunnel_opt, 30, ##ctx) \ FN(skb_change_proto, 31, ##ctx) \ FN(skb_change_type, 32, ##ctx) \ FN(skb_under_cgroup, 33, ##ctx) \ FN(get_hash_recalc, 34, ##ctx) \ FN(get_current_task, 35, ##ctx) \ FN(probe_write_user, 36, ##ctx) \ FN(current_task_under_cgroup, 37, ##ctx) \ FN(skb_change_tail, 38, ##ctx) \ FN(skb_pull_data, 39, ##ctx) \ FN(csum_update, 40, ##ctx) \ FN(set_hash_invalid, 41, ##ctx) \ FN(get_numa_node_id, 42, ##ctx) \ FN(skb_change_head, 43, ##ctx) \ FN(xdp_adjust_head, 44, ##ctx) \ FN(probe_read_str, 45, ##ctx) \ FN(get_socket_cookie, 46, ##ctx) \ FN(get_socket_uid, 47, ##ctx) \ FN(set_hash, 48, ##ctx) \ FN(setsockopt, 49, ##ctx) \ FN(skb_adjust_room, 50, ##ctx) \ FN(redirect_map, 51, ##ctx) \ FN(sk_redirect_map, 52, ##ctx) \ FN(sock_map_update, 53, ##ctx) \ FN(xdp_adjust_meta, 54, ##ctx) \ FN(perf_event_read_value, 55, ##ctx) \ FN(perf_prog_read_value, 56, ##ctx) \ FN(getsockopt, 57, ##ctx) \ FN(override_return, 58, ##ctx) \ FN(sock_ops_cb_flags_set, 59, ##ctx) \ FN(msg_redirect_map, 60, ##ctx) \ FN(msg_apply_bytes, 61, ##ctx) \ FN(msg_cork_bytes, 62, ##ctx) \ FN(msg_pull_data, 63, ##ctx) \ FN(bind, 64, ##ctx) \ FN(xdp_adjust_tail, 65, ##ctx) \ FN(skb_get_xfrm_state, 66, ##ctx) \ FN(get_stack, 67, ##ctx) \ FN(skb_load_bytes_relative, 68, ##ctx) \ FN(fib_lookup, 69, ##ctx) \ FN(sock_hash_update, 70, ##ctx) \ FN(msg_redirect_hash, 71, ##ctx) \ FN(sk_redirect_hash, 72, ##ctx) \ FN(lwt_push_encap, 73, ##ctx) \ FN(lwt_seg6_store_bytes, 74, ##ctx) \ FN(lwt_seg6_adjust_srh, 75, ##ctx) \ FN(lwt_seg6_action, 76, ##ctx) \ FN(rc_repeat, 77, ##ctx) \ FN(rc_keydown, 78, ##ctx) \ FN(skb_cgroup_id, 79, ##ctx) \ FN(get_current_cgroup_id, 80, ##ctx) \ FN(get_local_storage, 81, ##ctx) \ FN(sk_select_reuseport, 82, ##ctx) \ FN(skb_ancestor_cgroup_id, 83, ##ctx) \ FN(sk_lookup_tcp, 84, ##ctx) \ FN(sk_lookup_udp, 85, ##ctx) \ FN(sk_release, 86, ##ctx) \ FN(map_push_elem, 87, ##ctx) \ FN(map_pop_elem, 88, ##ctx) \ FN(map_peek_elem, 89, ##ctx) \ FN(msg_push_data, 90, ##ctx) \ FN(msg_pop_data, 91, ##ctx) \ FN(rc_pointer_rel, 92, ##ctx) \ FN(spin_lock, 93, ##ctx) \ FN(spin_unlock, 94, ##ctx) \ FN(sk_fullsock, 95, ##ctx) \ FN(tcp_sock, 96, ##ctx) \ FN(skb_ecn_set_ce, 97, ##ctx) \ FN(get_listener_sock, 98, ##ctx) \ FN(skc_lookup_tcp, 99, ##ctx) \ FN(tcp_check_syncookie, 100, ##ctx) \ FN(sysctl_get_name, 101, ##ctx) \ FN(sysctl_get_current_value, 102, ##ctx) \ FN(sysctl_get_new_value, 103, ##ctx) \ FN(sysctl_set_new_value, 104, ##ctx) \ FN(strtol, 105, ##ctx) \ FN(strtoul, 106, ##ctx) \ FN(sk_storage_get, 107, ##ctx) \ FN(sk_storage_delete, 108, ##ctx) \ FN(send_signal, 109, ##ctx) \ FN(tcp_gen_syncookie, 110, ##ctx) \ FN(skb_output, 111, ##ctx) \ FN(probe_read_user, 112, ##ctx) \ FN(probe_read_kernel, 113, ##ctx) \ FN(probe_read_user_str, 114, ##ctx) \ FN(probe_read_kernel_str, 115, ##ctx) \ FN(tcp_send_ack, 116, ##ctx) \ FN(send_signal_thread, 117, ##ctx) \ FN(jiffies64, 118, ##ctx) \ FN(read_branch_records, 119, ##ctx) \ FN(get_ns_current_pid_tgid, 120, ##ctx) \ FN(xdp_output, 121, ##ctx) \ FN(get_netns_cookie, 122, ##ctx) \ FN(get_current_ancestor_cgroup_id, 123, ##ctx) \ FN(sk_assign, 124, ##ctx) \ FN(ktime_get_boot_ns, 125, ##ctx) \ FN(seq_printf, 126, ##ctx) \ FN(seq_write, 127, ##ctx) \ FN(sk_cgroup_id, 128, ##ctx) \ FN(sk_ancestor_cgroup_id, 129, ##ctx) \ FN(ringbuf_output, 130, ##ctx) \ FN(ringbuf_reserve, 131, ##ctx) \ FN(ringbuf_submit, 132, ##ctx) \ FN(ringbuf_discard, 133, ##ctx) \ FN(ringbuf_query, 134, ##ctx) \ FN(csum_level, 135, ##ctx) \ FN(skc_to_tcp6_sock, 136, ##ctx) \ FN(skc_to_tcp_sock, 137, ##ctx) \ FN(skc_to_tcp_timewait_sock, 138, ##ctx) \ FN(skc_to_tcp_request_sock, 139, ##ctx) \ FN(skc_to_udp6_sock, 140, ##ctx) \ FN(get_task_stack, 141, ##ctx) \ FN(load_hdr_opt, 142, ##ctx) \ FN(store_hdr_opt, 143, ##ctx) \ FN(reserve_hdr_opt, 144, ##ctx) \ FN(inode_storage_get, 145, ##ctx) \ FN(inode_storage_delete, 146, ##ctx) \ FN(d_path, 147, ##ctx) \ FN(copy_from_user, 148, ##ctx) \ FN(snprintf_btf, 149, ##ctx) \ FN(seq_printf_btf, 150, ##ctx) \ FN(skb_cgroup_classid, 151, ##ctx) \ FN(redirect_neigh, 152, ##ctx) \ FN(per_cpu_ptr, 153, ##ctx) \ FN(this_cpu_ptr, 154, ##ctx) \ FN(redirect_peer, 155, ##ctx) \ FN(task_storage_get, 156, ##ctx) \ FN(task_storage_delete, 157, ##ctx) \ FN(get_current_task_btf, 158, ##ctx) \ FN(bprm_opts_set, 159, ##ctx) \ FN(ktime_get_coarse_ns, 160, ##ctx) \ FN(ima_inode_hash, 161, ##ctx) \ FN(sock_from_file, 162, ##ctx) \ FN(check_mtu, 163, ##ctx) \ FN(for_each_map_elem, 164, ##ctx) \ FN(snprintf, 165, ##ctx) \ FN(sys_bpf, 166, ##ctx) \ FN(btf_find_by_name_kind, 167, ##ctx) \ FN(sys_close, 168, ##ctx) \ FN(timer_init, 169, ##ctx) \ FN(timer_set_callback, 170, ##ctx) \ FN(timer_start, 171, ##ctx) \ FN(timer_cancel, 172, ##ctx) \ FN(get_func_ip, 173, ##ctx) \ FN(get_attach_cookie, 174, ##ctx) \ FN(task_pt_regs, 175, ##ctx) \ FN(get_branch_snapshot, 176, ##ctx) \ FN(trace_vprintk, 177, ##ctx) \ FN(skc_to_unix_sock, 178, ##ctx) \ FN(kallsyms_lookup_name, 179, ##ctx) \ FN(find_vma, 180, ##ctx) \ FN(loop, 181, ##ctx) \ FN(strncmp, 182, ##ctx) \ FN(get_func_arg, 183, ##ctx) \ FN(get_func_ret, 184, ##ctx) \ FN(get_func_arg_cnt, 185, ##ctx) \ FN(get_retval, 186, ##ctx) \ FN(set_retval, 187, ##ctx) \ FN(xdp_get_buff_len, 188, ##ctx) \ FN(xdp_load_bytes, 189, ##ctx) \ FN(xdp_store_bytes, 190, ##ctx) \ FN(copy_from_user_task, 191, ##ctx) \ FN(skb_set_tstamp, 192, ##ctx) \ FN(ima_file_hash, 193, ##ctx) \ FN(kptr_xchg, 194, ##ctx) \ FN(map_lookup_percpu_elem, 195, ##ctx) \ FN(skc_to_mptcp_sock, 196, ##ctx) \ FN(dynptr_from_mem, 197, ##ctx) \ FN(ringbuf_reserve_dynptr, 198, ##ctx) \ FN(ringbuf_submit_dynptr, 199, ##ctx) \ FN(ringbuf_discard_dynptr, 200, ##ctx) \ FN(dynptr_read, 201, ##ctx) \ FN(dynptr_write, 202, ##ctx) \ FN(dynptr_data, 203, ##ctx) \ FN(tcp_raw_gen_syncookie_ipv4, 204, ##ctx) \ FN(tcp_raw_gen_syncookie_ipv6, 205, ##ctx) \ FN(tcp_raw_check_syncookie_ipv4, 206, ##ctx) \ FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx) \ FN(ktime_get_tai_ns, 208, ##ctx) \ FN(user_ringbuf_drain, 209, ##ctx) \ FN(cgrp_storage_get, 210, ##ctx) \ FN(cgrp_storage_delete, 211, ##ctx) \ /* This helper list is effectively frozen. If you are trying to \ * add a new helper, you should add a kfunc instead which has \ * less stability guarantees. See Documentation/bpf/kfuncs.rst \ */ /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't * know or care about integer value that is now passed as second argument */ #define __BPF_FUNC_MAPPER_APPLY(name, value, FN) FN(name), #define __BPF_FUNC_MAPPER(FN) ___BPF_FUNC_MAPPER(__BPF_FUNC_MAPPER_APPLY, FN) /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call */ #define __BPF_ENUM_FN(x, y) BPF_FUNC_ ## x = y, enum bpf_func_id { ___BPF_FUNC_MAPPER(__BPF_ENUM_FN) __BPF_FUNC_MAX_ID, }; #undef __BPF_ENUM_FN /* All flags used by eBPF helper functions, placed here. */ /* BPF_FUNC_skb_store_bytes flags. */ enum { BPF_F_RECOMPUTE_CSUM = (1ULL << 0), BPF_F_INVALIDATE_HASH = (1ULL << 1), }; /* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags. * First 4 bits are for passing the header field size. */ enum { BPF_F_HDR_FIELD_MASK = 0xfULL, }; /* BPF_FUNC_l4_csum_replace flags. */ enum { BPF_F_PSEUDO_HDR = (1ULL << 4), BPF_F_MARK_MANGLED_0 = (1ULL << 5), BPF_F_MARK_ENFORCE = (1ULL << 6), BPF_F_IPV6 = (1ULL << 7), }; /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ enum { BPF_F_TUNINFO_IPV6 = (1ULL << 0), }; /* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */ enum { BPF_F_SKIP_FIELD_MASK = 0xffULL, BPF_F_USER_STACK = (1ULL << 8), /* flags used by BPF_FUNC_get_stackid only. */ BPF_F_FAST_STACK_CMP = (1ULL << 9), BPF_F_REUSE_STACKID = (1ULL << 10), /* flags used by BPF_FUNC_get_stack only. */ BPF_F_USER_BUILD_ID = (1ULL << 11), }; /* BPF_FUNC_skb_set_tunnel_key flags. */ enum { BPF_F_ZERO_CSUM_TX = (1ULL << 1), BPF_F_DONT_FRAGMENT = (1ULL << 2), BPF_F_SEQ_NUMBER = (1ULL << 3), BPF_F_NO_TUNNEL_KEY = (1ULL << 4), }; /* BPF_FUNC_skb_get_tunnel_key flags. */ enum { BPF_F_TUNINFO_FLAGS = (1ULL << 4), }; /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and * BPF_FUNC_perf_event_read_value flags. */ enum { BPF_F_INDEX_MASK = 0xffffffffULL, BPF_F_CURRENT_CPU = BPF_F_INDEX_MASK, /* BPF_FUNC_perf_event_output for sk_buff input context. */ BPF_F_CTXLEN_MASK = (0xfffffULL << 32), }; /* Current network namespace */ enum { BPF_F_CURRENT_NETNS = (-1L), }; /* BPF_FUNC_csum_level level values. */ enum { BPF_CSUM_LEVEL_QUERY, BPF_CSUM_LEVEL_INC, BPF_CSUM_LEVEL_DEC, BPF_CSUM_LEVEL_RESET, }; /* BPF_FUNC_skb_adjust_room flags. */ enum { BPF_F_ADJ_ROOM_FIXED_GSO = (1ULL << 0), BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = (1ULL << 1), BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = (1ULL << 2), BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3), BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4), BPF_F_ADJ_ROOM_NO_CSUM_RESET = (1ULL << 5), BPF_F_ADJ_ROOM_ENCAP_L2_ETH = (1ULL << 6), BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = (1ULL << 7), BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = (1ULL << 8), }; enum { BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff, BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56, }; #define BPF_F_ADJ_ROOM_ENCAP_L2(len) (((__u64)len & \ BPF_ADJ_ROOM_ENCAP_L2_MASK) \ << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) /* BPF_FUNC_sysctl_get_name flags. */ enum { BPF_F_SYSCTL_BASE_NAME = (1ULL << 0), }; /* BPF_FUNC__storage_get flags */ enum { BPF_LOCAL_STORAGE_GET_F_CREATE = (1ULL << 0), /* BPF_SK_STORAGE_GET_F_CREATE is only kept for backward compatibility * and BPF_LOCAL_STORAGE_GET_F_CREATE must be used instead. */ BPF_SK_STORAGE_GET_F_CREATE = BPF_LOCAL_STORAGE_GET_F_CREATE, }; /* BPF_FUNC_read_branch_records flags. */ enum { BPF_F_GET_BRANCH_RECORDS_SIZE = (1ULL << 0), }; /* BPF_FUNC_bpf_ringbuf_commit, BPF_FUNC_bpf_ringbuf_discard, and * BPF_FUNC_bpf_ringbuf_output flags. */ enum { BPF_RB_NO_WAKEUP = (1ULL << 0), BPF_RB_FORCE_WAKEUP = (1ULL << 1), }; /* BPF_FUNC_bpf_ringbuf_query flags */ enum { BPF_RB_AVAIL_DATA = 0, BPF_RB_RING_SIZE = 1, BPF_RB_CONS_POS = 2, BPF_RB_PROD_POS = 3, }; /* BPF ring buffer constants */ enum { BPF_RINGBUF_BUSY_BIT = (1U << 31), BPF_RINGBUF_DISCARD_BIT = (1U << 30), BPF_RINGBUF_HDR_SZ = 8, }; /* BPF_FUNC_sk_assign flags in bpf_sk_lookup context. */ enum { BPF_SK_LOOKUP_F_REPLACE = (1ULL << 0), BPF_SK_LOOKUP_F_NO_REUSEPORT = (1ULL << 1), }; /* Mode for BPF_FUNC_skb_adjust_room helper. */ enum bpf_adj_room_mode { BPF_ADJ_ROOM_NET, BPF_ADJ_ROOM_MAC, }; /* Mode for BPF_FUNC_skb_load_bytes_relative helper. */ enum bpf_hdr_start_off { BPF_HDR_START_MAC, BPF_HDR_START_NET, }; /* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */ enum bpf_lwt_encap_mode { BPF_LWT_ENCAP_SEG6, BPF_LWT_ENCAP_SEG6_INLINE, BPF_LWT_ENCAP_IP, }; /* Flags for bpf_bprm_opts_set helper */ enum { BPF_F_BPRM_SECUREEXEC = (1ULL << 0), }; /* Flags for bpf_redirect and bpf_redirect_map helpers */ enum { BPF_F_INGRESS = (1ULL << 0), /* used for skb path */ BPF_F_BROADCAST = (1ULL << 3), /* used for XDP path */ BPF_F_EXCLUDE_INGRESS = (1ULL << 4), /* used for XDP path */ #define BPF_F_REDIRECT_FLAGS (BPF_F_INGRESS | BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) }; #define __bpf_md_ptr(type, name) \ union { \ type name; \ __u64 :64; \ } __attribute__((aligned(8))) /* The enum used in skb->tstamp_type. It specifies the clock type * of the time stored in the skb->tstamp. */ enum { BPF_SKB_TSTAMP_UNSPEC = 0, /* DEPRECATED */ BPF_SKB_TSTAMP_DELIVERY_MONO = 1, /* DEPRECATED */ BPF_SKB_CLOCK_REALTIME = 0, BPF_SKB_CLOCK_MONOTONIC = 1, BPF_SKB_CLOCK_TAI = 2, /* For any future BPF_SKB_CLOCK_* that the bpf prog cannot handle, * the bpf prog can try to deduce it by ingress/egress/skb->sk->sk_clockid. */ }; /* user accessible mirror of in-kernel sk_buff. * new fields can only be added to the end of this structure */ struct __sk_buff { __u32 len; __u32 pkt_type; __u32 mark; __u32 queue_mapping; __u32 protocol; __u32 vlan_present; __u32 vlan_tci; __u32 vlan_proto; __u32 priority; __u32 ingress_ifindex; __u32 ifindex; __u32 tc_index; __u32 cb[5]; __u32 hash; __u32 tc_classid; __u32 data; __u32 data_end; __u32 napi_id; /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ /* ... here. */ __u32 data_meta; __bpf_md_ptr(struct bpf_flow_keys *, flow_keys); __u64 tstamp; __u32 wire_len; __u32 gso_segs; __bpf_md_ptr(struct bpf_sock *, sk); __u32 gso_size; __u8 tstamp_type; __u32 :24; /* Padding, future use. */ __u64 hwtstamp; }; struct bpf_tunnel_key { __u32 tunnel_id; union { __u32 remote_ipv4; __u32 remote_ipv6[4]; }; __u8 tunnel_tos; __u8 tunnel_ttl; union { __u16 tunnel_ext; /* compat */ __be16 tunnel_flags; }; __u32 tunnel_label; union { __u32 local_ipv4; __u32 local_ipv6[4]; }; }; /* user accessible mirror of in-kernel xfrm_state. * new fields can only be added to the end of this structure */ struct bpf_xfrm_state { __u32 reqid; __u32 spi; /* Stored in network byte order */ __u16 family; __u16 ext; /* Padding, future use. */ union { __u32 remote_ipv4; /* Stored in network byte order */ __u32 remote_ipv6[4]; /* Stored in network byte order */ }; }; /* Generic BPF return codes which all BPF program types may support. * The values are binary compatible with their TC_ACT_* counter-part to * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT * programs. * * XDP is handled seprately, see XDP_*. */ enum bpf_ret_code { BPF_OK = 0, /* 1 reserved */ BPF_DROP = 2, /* 3-6 reserved */ BPF_REDIRECT = 7, /* >127 are reserved for prog type specific return codes. * * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and * BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been * changed and should be routed based on its new L3 header. * (This is an L3 redirect, as opposed to L2 redirect * represented by BPF_REDIRECT above). */ BPF_LWT_REROUTE = 128, /* BPF_FLOW_DISSECTOR_CONTINUE: used by BPF_PROG_TYPE_FLOW_DISSECTOR * to indicate that no custom dissection was performed, and * fallback to standard dissector is requested. */ BPF_FLOW_DISSECTOR_CONTINUE = 129, }; struct bpf_sock { __u32 bound_dev_if; __u32 family; __u32 type; __u32 protocol; __u32 mark; __u32 priority; /* IP address also allows 1 and 2 bytes access */ __u32 src_ip4; __u32 src_ip6[4]; __u32 src_port; /* host byte order */ __be16 dst_port; /* network byte order */ __u16 :16; /* zero padding */ __u32 dst_ip4; __u32 dst_ip6[4]; __u32 state; __s32 rx_queue_mapping; }; struct bpf_tcp_sock { __u32 snd_cwnd; /* Sending congestion window */ __u32 srtt_us; /* smoothed round trip time << 3 in usecs */ __u32 rtt_min; __u32 snd_ssthresh; /* Slow start size threshold */ __u32 rcv_nxt; /* What we want to receive next */ __u32 snd_nxt; /* Next sequence we send */ __u32 snd_una; /* First byte we want an ack for */ __u32 mss_cache; /* Cached effective mss, not including SACKS */ __u32 ecn_flags; /* ECN status bits. */ __u32 rate_delivered; /* saved rate sample: packets delivered */ __u32 rate_interval_us; /* saved rate sample: time elapsed */ __u32 packets_out; /* Packets which are "in flight" */ __u32 retrans_out; /* Retransmitted packets out */ __u32 total_retrans; /* Total retransmits for entire connection */ __u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn * total number of segments in. */ __u32 data_segs_in; /* RFC4898 tcpEStatsPerfDataSegsIn * total number of data segments in. */ __u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut * The total number of segments sent. */ __u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut * total number of data segments sent. */ __u32 lost_out; /* Lost packets */ __u32 sacked_out; /* SACK'd packets */ __u64 bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived * sum(delta(rcv_nxt)), or how many bytes * were acked. */ __u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked * sum(delta(snd_una)), or how many bytes * were acked. */ __u32 dsack_dups; /* RFC4898 tcpEStatsStackDSACKDups * total number of DSACK blocks received */ __u32 delivered; /* Total data packets delivered incl. rexmits */ __u32 delivered_ce; /* Like the above but only ECE marked packets */ __u32 icsk_retransmits; /* Number of unrecovered [RTO] timeouts */ }; struct bpf_sock_tuple { union { struct { __be32 saddr; __be32 daddr; __be16 sport; __be16 dport; } ipv4; struct { __be32 saddr[4]; __be32 daddr[4]; __be16 sport; __be16 dport; } ipv6; }; }; /* (Simplified) user return codes for tcx prog type. * A valid tcx program must return one of these defined values. All other * return codes are reserved for future use. Must remain compatible with * their TC_ACT_* counter-parts. For compatibility in behavior, unknown * return codes are mapped to TCX_NEXT. */ enum tcx_action_base { TCX_NEXT = -1, TCX_PASS = 0, TCX_DROP = 2, TCX_REDIRECT = 7, }; struct bpf_xdp_sock { __u32 queue_id; }; #define XDP_PACKET_HEADROOM 256 /* User return codes for XDP prog type. * A valid XDP program must return one of these defined values. All other * return codes are reserved for future use. Unknown return codes will * result in packet drops and a warning via bpf_warn_invalid_xdp_action(). */ enum xdp_action { XDP_ABORTED = 0, XDP_DROP, XDP_PASS, XDP_TX, XDP_REDIRECT, }; /* user accessible metadata for XDP packet hook * new fields must be added to the end of this structure */ struct xdp_md { __u32 data; __u32 data_end; __u32 data_meta; /* Below access go through struct xdp_rxq_info */ __u32 ingress_ifindex; /* rxq->dev->ifindex */ __u32 rx_queue_index; /* rxq->queue_index */ __u32 egress_ifindex; /* txq->dev->ifindex */ }; /* DEVMAP map-value layout * * The struct data-layout of map-value is a configuration interface. * New members can only be added to the end of this structure. */ struct bpf_devmap_val { __u32 ifindex; /* device index */ union { int fd; /* prog fd on map write */ __u32 id; /* prog id on map read */ } bpf_prog; }; /* CPUMAP map-value layout * * The struct data-layout of map-value is a configuration interface. * New members can only be added to the end of this structure. */ struct bpf_cpumap_val { __u32 qsize; /* queue size to remote target CPU */ union { int fd; /* prog fd on map write */ __u32 id; /* prog id on map read */ } bpf_prog; }; enum sk_action { SK_DROP = 0, SK_PASS, }; /* user accessible metadata for SK_MSG packet hook, new fields must * be added to the end of this structure */ struct sk_msg_md { __bpf_md_ptr(void *, data); __bpf_md_ptr(void *, data_end); __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ __u32 size; /* Total size of sk_msg */ __bpf_md_ptr(struct bpf_sock *, sk); /* current socket */ }; struct sk_reuseport_md { /* * Start of directly accessible data. It begins from * the tcp/udp header. */ __bpf_md_ptr(void *, data); /* End of directly accessible data */ __bpf_md_ptr(void *, data_end); /* * Total length of packet (starting from the tcp/udp header). * Note that the directly accessible bytes (data_end - data) * could be less than this "len". Those bytes could be * indirectly read by a helper "bpf_skb_load_bytes()". */ __u32 len; /* * Eth protocol in the mac header (network byte order). e.g. * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD) */ __u32 eth_protocol; __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */ __u32 bind_inany; /* Is sock bound to an INANY address? */ __u32 hash; /* A hash of the packet 4 tuples */ /* When reuse->migrating_sk is NULL, it is selecting a sk for the * new incoming connection request (e.g. selecting a listen sk for * the received SYN in the TCP case). reuse->sk is one of the sk * in the reuseport group. The bpf prog can use reuse->sk to learn * the local listening ip/port without looking into the skb. * * When reuse->migrating_sk is not NULL, reuse->sk is closed and * reuse->migrating_sk is the socket that needs to be migrated * to another listening socket. migrating_sk could be a fullsock * sk that is fully established or a reqsk that is in-the-middle * of 3-way handshake. */ __bpf_md_ptr(struct bpf_sock *, sk); __bpf_md_ptr(struct bpf_sock *, migrating_sk); }; #define BPF_TAG_SIZE 8 struct bpf_prog_info { __u32 type; __u32 id; __u8 tag[BPF_TAG_SIZE]; __u32 jited_prog_len; __u32 xlated_prog_len; __aligned_u64 jited_prog_insns; __aligned_u64 xlated_prog_insns; __u64 load_time; /* ns since boottime */ __u32 created_by_uid; __u32 nr_map_ids; __aligned_u64 map_ids; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; __u32 gpl_compatible:1; __u32 :31; /* alignment pad */ __u64 netns_dev; __u64 netns_ino; __u32 nr_jited_ksyms; __u32 nr_jited_func_lens; __aligned_u64 jited_ksyms; __aligned_u64 jited_func_lens; __u32 btf_id; __u32 func_info_rec_size; __aligned_u64 func_info; __u32 nr_func_info; __u32 nr_line_info; __aligned_u64 line_info; __aligned_u64 jited_line_info; __u32 nr_jited_line_info; __u32 line_info_rec_size; __u32 jited_line_info_rec_size; __u32 nr_prog_tags; __aligned_u64 prog_tags; __u64 run_time_ns; __u64 run_cnt; __u64 recursion_misses; __u32 verified_insns; __u32 attach_btf_obj_id; __u32 attach_btf_id; } __attribute__((aligned(8))); struct bpf_map_info { __u32 type; __u32 id; __u32 key_size; __u32 value_size; __u32 max_entries; __u32 map_flags; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; __u32 btf_vmlinux_value_type_id; __u64 netns_dev; __u64 netns_ino; __u32 btf_id; __u32 btf_key_type_id; __u32 btf_value_type_id; __u32 btf_vmlinux_id; __u64 map_extra; } __attribute__((aligned(8))); struct bpf_btf_info { __aligned_u64 btf; __u32 btf_size; __u32 id; __aligned_u64 name; __u32 name_len; __u32 kernel_btf; } __attribute__((aligned(8))); struct bpf_link_info { __u32 type; __u32 id; __u32 prog_id; union { struct { __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */ __u32 tp_name_len; /* in/out: tp_name buffer len */ } raw_tracepoint; struct { __u32 attach_type; __u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */ __u32 target_btf_id; /* BTF type id inside the object */ } tracing; struct { __u64 cgroup_id; __u32 attach_type; } cgroup; struct { __aligned_u64 target_name; /* in/out: target_name buffer ptr */ __u32 target_name_len; /* in/out: target_name buffer len */ /* If the iter specific field is 32 bits, it can be put * in the first or second union. Otherwise it should be * put in the second union. */ union { struct { __u32 map_id; } map; }; union { struct { __u64 cgroup_id; __u32 order; } cgroup; struct { __u32 tid; __u32 pid; } task; }; } iter; struct { __u32 netns_ino; __u32 attach_type; } netns; struct { __u32 ifindex; } xdp; struct { __u32 map_id; } struct_ops; struct { __u32 pf; __u32 hooknum; __s32 priority; __u32 flags; } netfilter; struct { __aligned_u64 addrs; __u32 count; /* in/out: kprobe_multi function count */ __u32 flags; __u64 missed; __aligned_u64 cookies; } kprobe_multi; struct { __aligned_u64 path; __aligned_u64 offsets; __aligned_u64 ref_ctr_offsets; __aligned_u64 cookies; __u32 path_size; /* in/out: real path size on success, including zero byte */ __u32 count; /* in/out: uprobe_multi offsets/ref_ctr_offsets/cookies count */ __u32 flags; __u32 pid; } uprobe_multi; struct { __u32 type; /* enum bpf_perf_event_type */ __u32 :32; union { struct { __aligned_u64 file_name; /* in/out */ __u32 name_len; __u32 offset; /* offset from file_name */ __u64 cookie; __u64 ref_ctr_offset; } uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */ struct { __aligned_u64 func_name; /* in/out */ __u32 name_len; __u32 offset; /* offset from func_name */ __u64 addr; __u64 missed; __u64 cookie; } kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */ struct { __aligned_u64 tp_name; /* in/out */ __u32 name_len; __u32 :32; __u64 cookie; } tracepoint; /* BPF_PERF_EVENT_TRACEPOINT */ struct { __u64 config; __u32 type; __u32 :32; __u64 cookie; } event; /* BPF_PERF_EVENT_EVENT */ }; } perf_event; struct { __u32 ifindex; __u32 attach_type; } tcx; struct { __u32 ifindex; __u32 attach_type; } netkit; struct { __u32 map_id; __u32 attach_type; } sockmap; }; } __attribute__((aligned(8))); /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed * by user and intended to be used by socket (e.g. to bind to, depends on * attach type). */ struct bpf_sock_addr { __u32 user_family; /* Allows 4-byte read, but no write. */ __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order. */ __u32 user_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write. * Stored in network byte order. */ __u32 user_port; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order */ __u32 family; /* Allows 4-byte read, but no write */ __u32 type; /* Allows 4-byte read, but no write */ __u32 protocol; /* Allows 4-byte read, but no write */ __u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order. */ __u32 msg_src_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write. * Stored in network byte order. */ __bpf_md_ptr(struct bpf_sock *, sk); }; /* User bpf_sock_ops struct to access socket values and specify request ops * and their replies. * Some of this fields are in network (bigendian) byte order and may need * to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h). * New fields can only be added at the end of this structure */ struct bpf_sock_ops { __u32 op; union { __u32 args[4]; /* Optionally passed to bpf program */ __u32 reply; /* Returned by bpf program */ __u32 replylong[4]; /* Optionally returned by bpf prog */ }; __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ __u32 is_fullsock; /* Some TCP fields are only valid if * there is a full socket. If not, the * fields read as zero. */ __u32 snd_cwnd; __u32 srtt_us; /* Averaged RTT << 3 in usecs */ __u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */ __u32 state; __u32 rtt_min; __u32 snd_ssthresh; __u32 rcv_nxt; __u32 snd_nxt; __u32 snd_una; __u32 mss_cache; __u32 ecn_flags; __u32 rate_delivered; __u32 rate_interval_us; __u32 packets_out; __u32 retrans_out; __u32 total_retrans; __u32 segs_in; __u32 data_segs_in; __u32 segs_out; __u32 data_segs_out; __u32 lost_out; __u32 sacked_out; __u32 sk_txhash; __u64 bytes_received; __u64 bytes_acked; __bpf_md_ptr(struct bpf_sock *, sk); /* [skb_data, skb_data_end) covers the whole TCP header. * * BPF_SOCK_OPS_PARSE_HDR_OPT_CB: The packet received * BPF_SOCK_OPS_HDR_OPT_LEN_CB: Not useful because the * header has not been written. * BPF_SOCK_OPS_WRITE_HDR_OPT_CB: The header and options have * been written so far. * BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: The SYNACK that concludes * the 3WHS. * BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: The ACK that concludes * the 3WHS. * * bpf_load_hdr_opt() can also be used to read a particular option. */ __bpf_md_ptr(void *, skb_data); __bpf_md_ptr(void *, skb_data_end); __u32 skb_len; /* The total length of a packet. * It includes the header, options, * and payload. */ __u32 skb_tcp_flags; /* tcp_flags of the header. It provides * an easy way to check for tcp_flags * without parsing skb_data. * * In particular, the skb_tcp_flags * will still be available in * BPF_SOCK_OPS_HDR_OPT_LEN even though * the outgoing header has not * been written yet. */ __u64 skb_hwtstamp; }; /* Definitions for bpf_sock_ops_cb_flags */ enum { BPF_SOCK_OPS_RTO_CB_FLAG = (1<<0), BPF_SOCK_OPS_RETRANS_CB_FLAG = (1<<1), BPF_SOCK_OPS_STATE_CB_FLAG = (1<<2), BPF_SOCK_OPS_RTT_CB_FLAG = (1<<3), /* Call bpf for all received TCP headers. The bpf prog will be * called under sock_ops->op == BPF_SOCK_OPS_PARSE_HDR_OPT_CB * * Please refer to the comment in BPF_SOCK_OPS_PARSE_HDR_OPT_CB * for the header option related helpers that will be useful * to the bpf programs. * * It could be used at the client/active side (i.e. connect() side) * when the server told it that the server was in syncookie * mode and required the active side to resend the bpf-written * options. The active side can keep writing the bpf-options until * it received a valid packet from the server side to confirm * the earlier packet (and options) has been received. The later * example patch is using it like this at the active side when the * server is in syncookie mode. * * The bpf prog will usually turn this off in the common cases. */ BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG = (1<<4), /* Call bpf when kernel has received a header option that * the kernel cannot handle. The bpf prog will be called under * sock_ops->op == BPF_SOCK_OPS_PARSE_HDR_OPT_CB. * * Please refer to the comment in BPF_SOCK_OPS_PARSE_HDR_OPT_CB * for the header option related helpers that will be useful * to the bpf programs. */ BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = (1<<5), /* Call bpf when the kernel is writing header options for the * outgoing packet. The bpf prog will first be called * to reserve space in a skb under * sock_ops->op == BPF_SOCK_OPS_HDR_OPT_LEN_CB. Then * the bpf prog will be called to write the header option(s) * under sock_ops->op == BPF_SOCK_OPS_WRITE_HDR_OPT_CB. * * Please refer to the comment in BPF_SOCK_OPS_HDR_OPT_LEN_CB * and BPF_SOCK_OPS_WRITE_HDR_OPT_CB for the header option * related helpers that will be useful to the bpf programs. * * The kernel gets its chance to reserve space and write * options first before the BPF program does. */ BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG = (1<<6), /* Mask of all currently supported cb flags */ BPF_SOCK_OPS_ALL_CB_FLAGS = 0x7F, }; enum { SK_BPF_CB_TX_TIMESTAMPING = 1<<0, SK_BPF_CB_MASK = (SK_BPF_CB_TX_TIMESTAMPING - 1) | SK_BPF_CB_TX_TIMESTAMPING }; /* List of known BPF sock_ops operators. * New entries can only be added at the end */ enum { BPF_SOCK_OPS_VOID, BPF_SOCK_OPS_TIMEOUT_INIT, /* Should return SYN-RTO value to use or * -1 if default value should be used */ BPF_SOCK_OPS_RWND_INIT, /* Should return initial advertized * window (in packets) or -1 if default * value should be used */ BPF_SOCK_OPS_TCP_CONNECT_CB, /* Calls BPF program right before an * active connection is initialized */ BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, /* Calls BPF program when an * active connection is * established */ BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, /* Calls BPF program when a * passive connection is * established */ BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control * needs ECN */ BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is * based on the path and may be * dependent on the congestion control * algorithm. In general it indicates * a congestion threshold. RTTs above * this indicate congestion */ BPF_SOCK_OPS_RTO_CB, /* Called when an RTO has triggered. * Arg1: value of icsk_retransmits * Arg2: value of icsk_rto * Arg3: whether RTO has expired */ BPF_SOCK_OPS_RETRANS_CB, /* Called when skb is retransmitted. * Arg1: sequence number of 1st byte * Arg2: # segments * Arg3: return value of * tcp_transmit_skb (0 => success) */ BPF_SOCK_OPS_STATE_CB, /* Called when TCP changes state. * Arg1: old_state * Arg2: new_state */ BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after * socket transition to LISTEN state. */ BPF_SOCK_OPS_RTT_CB, /* Called on every RTT. * Arg1: measured RTT input (mrtt) * Arg2: updated srtt */ BPF_SOCK_OPS_PARSE_HDR_OPT_CB, /* Parse the header option. * It will be called to handle * the packets received at * an already established * connection. * * sock_ops->skb_data: * Referring to the received skb. * It covers the TCP header only. * * bpf_load_hdr_opt() can also * be used to search for a * particular option. */ BPF_SOCK_OPS_HDR_OPT_LEN_CB, /* Reserve space for writing the * header option later in * BPF_SOCK_OPS_WRITE_HDR_OPT_CB. * Arg1: bool want_cookie. (in * writing SYNACK only) * * sock_ops->skb_data: * Not available because no header has * been written yet. * * sock_ops->skb_tcp_flags: * The tcp_flags of the * outgoing skb. (e.g. SYN, ACK, FIN). * * bpf_reserve_hdr_opt() should * be used to reserve space. */ BPF_SOCK_OPS_WRITE_HDR_OPT_CB, /* Write the header options * Arg1: bool want_cookie. (in * writing SYNACK only) * * sock_ops->skb_data: * Referring to the outgoing skb. * It covers the TCP header * that has already been written * by the kernel and the * earlier bpf-progs. * * sock_ops->skb_tcp_flags: * The tcp_flags of the outgoing * skb. (e.g. SYN, ACK, FIN). * * bpf_store_hdr_opt() should * be used to write the * option. * * bpf_load_hdr_opt() can also * be used to search for a * particular option that * has already been written * by the kernel or the * earlier bpf-progs. */ BPF_SOCK_OPS_TSTAMP_SCHED_CB, /* Called when skb is passing * through dev layer when * SK_BPF_CB_TX_TIMESTAMPING * feature is on. */ BPF_SOCK_OPS_TSTAMP_SND_SW_CB, /* Called when skb is about to send * to the nic when SK_BPF_CB_TX_TIMESTAMPING * feature is on. */ BPF_SOCK_OPS_TSTAMP_SND_HW_CB, /* Called in hardware phase when * SK_BPF_CB_TX_TIMESTAMPING feature * is on. */ BPF_SOCK_OPS_TSTAMP_ACK_CB, /* Called when all the skbs in the * same sendmsg call are acked * when SK_BPF_CB_TX_TIMESTAMPING * feature is on. */ BPF_SOCK_OPS_TSTAMP_SENDMSG_CB, /* Called when every sendmsg syscall * is triggered. It's used to correlate * sendmsg timestamp with corresponding * tskey. */ }; /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect * changes between the TCP and BPF versions. Ideally this should never happen. * If it does, we need to add code to convert them before calling * the BPF sock_ops function. */ enum { BPF_TCP_ESTABLISHED = 1, BPF_TCP_SYN_SENT, BPF_TCP_SYN_RECV, BPF_TCP_FIN_WAIT1, BPF_TCP_FIN_WAIT2, BPF_TCP_TIME_WAIT, BPF_TCP_CLOSE, BPF_TCP_CLOSE_WAIT, BPF_TCP_LAST_ACK, BPF_TCP_LISTEN, BPF_TCP_CLOSING, /* Now a valid state */ BPF_TCP_NEW_SYN_RECV, BPF_TCP_BOUND_INACTIVE, BPF_TCP_MAX_STATES /* Leave at the end! */ }; enum { TCP_BPF_IW = 1001, /* Set TCP initial congestion window */ TCP_BPF_SNDCWND_CLAMP = 1002, /* Set sndcwnd_clamp */ TCP_BPF_DELACK_MAX = 1003, /* Max delay ack in usecs */ TCP_BPF_RTO_MIN = 1004, /* Min delay ack in usecs */ /* Copy the SYN pkt to optval * * BPF_PROG_TYPE_SOCK_OPS only. It is similar to the * bpf_getsockopt(TCP_SAVED_SYN) but it does not limit * to only getting from the saved_syn. It can either get the * syn packet from: * * 1. the just-received SYN packet (only available when writing the * SYNACK). It will be useful when it is not necessary to * save the SYN packet for latter use. It is also the only way * to get the SYN during syncookie mode because the syn * packet cannot be saved during syncookie. * * OR * * 2. the earlier saved syn which was done by * bpf_setsockopt(TCP_SAVE_SYN). * * The bpf_getsockopt(TCP_BPF_SYN*) option will hide where the * SYN packet is obtained. * * If the bpf-prog does not need the IP[46] header, the * bpf-prog can avoid parsing the IP header by using * TCP_BPF_SYN. Otherwise, the bpf-prog can get both * IP[46] and TCP header by using TCP_BPF_SYN_IP. * * >0: Total number of bytes copied * -ENOSPC: Not enough space in optval. Only optlen number of * bytes is copied. * -ENOENT: The SYN skb is not available now and the earlier SYN pkt * is not saved by setsockopt(TCP_SAVE_SYN). */ TCP_BPF_SYN = 1005, /* Copy the TCP header */ TCP_BPF_SYN_IP = 1006, /* Copy the IP[46] and TCP header */ TCP_BPF_SYN_MAC = 1007, /* Copy the MAC, IP[46], and TCP header */ TCP_BPF_SOCK_OPS_CB_FLAGS = 1008, /* Get or Set TCP sock ops flags */ SK_BPF_CB_FLAGS = 1009, /* Get or set sock ops flags in socket */ }; enum { BPF_LOAD_HDR_OPT_TCP_SYN = (1ULL << 0), }; /* args[0] value during BPF_SOCK_OPS_HDR_OPT_LEN_CB and * BPF_SOCK_OPS_WRITE_HDR_OPT_CB. */ enum { BPF_WRITE_HDR_TCP_CURRENT_MSS = 1, /* Kernel is finding the * total option spaces * required for an established * sk in order to calculate the * MSS. No skb is actually * sent. */ BPF_WRITE_HDR_TCP_SYNACK_COOKIE = 2, /* Kernel is in syncookie mode * when sending a SYN. */ }; struct bpf_perf_event_value { __u64 counter; __u64 enabled; __u64 running; }; enum { BPF_DEVCG_ACC_MKNOD = (1ULL << 0), BPF_DEVCG_ACC_READ = (1ULL << 1), BPF_DEVCG_ACC_WRITE = (1ULL << 2), }; enum { BPF_DEVCG_DEV_BLOCK = (1ULL << 0), BPF_DEVCG_DEV_CHAR = (1ULL << 1), }; struct bpf_cgroup_dev_ctx { /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */ __u32 access_type; __u32 major; __u32 minor; }; struct bpf_raw_tracepoint_args { __u64 args[0]; }; /* DIRECT: Skip the FIB rules and go to FIB table associated with device * OUTPUT: Do lookup from egress perspective; default is ingress */ enum { BPF_FIB_LOOKUP_DIRECT = (1U << 0), BPF_FIB_LOOKUP_OUTPUT = (1U << 1), BPF_FIB_LOOKUP_SKIP_NEIGH = (1U << 2), BPF_FIB_LOOKUP_TBID = (1U << 3), BPF_FIB_LOOKUP_SRC = (1U << 4), BPF_FIB_LOOKUP_MARK = (1U << 5), }; enum { BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */ BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */ BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */ BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */ BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */ BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */ BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */ BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */ BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */ BPF_FIB_LKUP_RET_NO_SRC_ADDR, /* failed to derive IP src addr */ }; struct bpf_fib_lookup { /* input: network family for lookup (AF_INET, AF_INET6) * output: network family of egress nexthop */ __u8 family; /* set if lookup is to consider L4 data - e.g., FIB rules */ __u8 l4_protocol; __be16 sport; __be16 dport; union { /* used for MTU check */ /* input to lookup */ __u16 tot_len; /* L3 length from network hdr (iph->tot_len) */ /* output: MTU value */ __u16 mtu_result; } __attribute__((packed, aligned(2))); /* input: L3 device index for lookup * output: device index from FIB lookup */ __u32 ifindex; union { /* inputs to lookup */ __u8 tos; /* AF_INET */ __be32 flowinfo; /* AF_INET6, flow_label + priority */ /* output: metric of fib result (IPv4/IPv6 only) */ __u32 rt_metric; }; /* input: source address to consider for lookup * output: source address result from lookup */ union { __be32 ipv4_src; __u32 ipv6_src[4]; /* in6_addr; network order */ }; /* input to bpf_fib_lookup, ipv{4,6}_dst is destination address in * network header. output: bpf_fib_lookup sets to gateway address * if FIB lookup returns gateway route */ union { __be32 ipv4_dst; __u32 ipv6_dst[4]; /* in6_addr; network order */ }; union { struct { /* output */ __be16 h_vlan_proto; __be16 h_vlan_TCI; }; /* input: when accompanied with the * 'BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID` flags, a * specific routing table to use for the fib lookup. */ __u32 tbid; }; union { /* input */ struct { __u32 mark; /* policy routing */ /* 2 4-byte holes for input */ }; /* output: source and dest mac */ struct { __u8 smac[6]; /* ETH_ALEN */ __u8 dmac[6]; /* ETH_ALEN */ }; }; }; struct bpf_redir_neigh { /* network family for lookup (AF_INET, AF_INET6) */ __u32 nh_family; /* network address of nexthop; skips fib lookup to find gateway */ union { __be32 ipv4_nh; __u32 ipv6_nh[4]; /* in6_addr; network order */ }; }; /* bpf_check_mtu flags*/ enum bpf_check_mtu_flags { BPF_MTU_CHK_SEGS = (1U << 0), }; enum bpf_check_mtu_ret { BPF_MTU_CHK_RET_SUCCESS, /* check and lookup successful */ BPF_MTU_CHK_RET_FRAG_NEEDED, /* fragmentation required to fwd */ BPF_MTU_CHK_RET_SEGS_TOOBIG, /* GSO re-segmentation needed to fwd */ }; enum bpf_task_fd_type { BPF_FD_TYPE_RAW_TRACEPOINT, /* tp name */ BPF_FD_TYPE_TRACEPOINT, /* tp name */ BPF_FD_TYPE_KPROBE, /* (symbol + offset) or addr */ BPF_FD_TYPE_KRETPROBE, /* (symbol + offset) or addr */ BPF_FD_TYPE_UPROBE, /* filename + offset */ BPF_FD_TYPE_URETPROBE, /* filename + offset */ }; enum { BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = (1U << 0), BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = (1U << 1), BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = (1U << 2), }; struct bpf_flow_keys { __u16 nhoff; __u16 thoff; __u16 addr_proto; /* ETH_P_* of valid addrs */ __u8 is_frag; __u8 is_first_frag; __u8 is_encap; __u8 ip_proto; __be16 n_proto; __be16 sport; __be16 dport; union { struct { __be32 ipv4_src; __be32 ipv4_dst; }; struct { __u32 ipv6_src[4]; /* in6_addr; network order */ __u32 ipv6_dst[4]; /* in6_addr; network order */ }; }; __u32 flags; __be32 flow_label; }; struct bpf_func_info { __u32 insn_off; __u32 type_id; }; #define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10) #define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff) struct bpf_line_info { __u32 insn_off; __u32 file_name_off; __u32 line_off; __u32 line_col; }; struct bpf_spin_lock { __u32 val; }; struct bpf_timer { __u64 __opaque[2]; } __attribute__((aligned(8))); struct bpf_wq { __u64 __opaque[2]; } __attribute__((aligned(8))); struct bpf_dynptr { __u64 __opaque[2]; } __attribute__((aligned(8))); struct bpf_list_head { __u64 __opaque[2]; } __attribute__((aligned(8))); struct bpf_list_node { __u64 __opaque[3]; } __attribute__((aligned(8))); struct bpf_rb_root { __u64 __opaque[2]; } __attribute__((aligned(8))); struct bpf_rb_node { __u64 __opaque[4]; } __attribute__((aligned(8))); struct bpf_refcount { __u32 __opaque[1]; } __attribute__((aligned(4))); struct bpf_sysctl { __u32 write; /* Sysctl is being read (= 0) or written (= 1). * Allows 1,2,4-byte read, but no write. */ __u32 file_pos; /* Sysctl file position to read from, write to. * Allows 1,2,4-byte read an 4-byte write. */ }; struct bpf_sockopt { __bpf_md_ptr(struct bpf_sock *, sk); __bpf_md_ptr(void *, optval); __bpf_md_ptr(void *, optval_end); __s32 level; __s32 optname; __s32 optlen; __s32 retval; }; struct bpf_pidns_info { __u32 pid; __u32 tgid; }; /* User accessible data for SK_LOOKUP programs. Add new fields at the end. */ struct bpf_sk_lookup { union { __bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */ __u64 cookie; /* Non-zero if socket was selected in PROG_TEST_RUN */ }; __u32 family; /* Protocol family (AF_INET, AF_INET6) */ __u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */ __u32 remote_ip4; /* Network byte order */ __u32 remote_ip6[4]; /* Network byte order */ __be16 remote_port; /* Network byte order */ __u16 :16; /* Zero padding */ __u32 local_ip4; /* Network byte order */ __u32 local_ip6[4]; /* Network byte order */ __u32 local_port; /* Host byte order */ __u32 ingress_ifindex; /* The arriving interface. Determined by inet_iif. */ }; /* * struct btf_ptr is used for typed pointer representation; the * type id is used to render the pointer data as the appropriate type * via the bpf_snprintf_btf() helper described above. A flags field - * potentially to specify additional details about the BTF pointer * (rather than its mode of display) - is included for future use. * Display flags - BTF_F_* - are passed to bpf_snprintf_btf separately. */ struct btf_ptr { void *ptr; __u32 type_id; __u32 flags; /* BTF ptr flags; unused at present. */ }; /* * Flags to control bpf_snprintf_btf() behaviour. * - BTF_F_COMPACT: no formatting around type information * - BTF_F_NONAME: no struct/union member names/types * - BTF_F_PTR_RAW: show raw (unobfuscated) pointer values; * equivalent to %px. * - BTF_F_ZERO: show zero-valued struct/union members; they * are not displayed by default */ enum { BTF_F_COMPACT = (1ULL << 0), BTF_F_NONAME = (1ULL << 1), BTF_F_PTR_RAW = (1ULL << 2), BTF_F_ZERO = (1ULL << 3), }; /* bpf_core_relo_kind encodes which aspect of captured field/type/enum value * has to be adjusted by relocations. It is emitted by llvm and passed to * libbpf and later to the kernel. */ enum bpf_core_relo_kind { BPF_CORE_FIELD_BYTE_OFFSET = 0, /* field byte offset */ BPF_CORE_FIELD_BYTE_SIZE = 1, /* field size in bytes */ BPF_CORE_FIELD_EXISTS = 2, /* field existence in target kernel */ BPF_CORE_FIELD_SIGNED = 3, /* field signedness (0 - unsigned, 1 - signed) */ BPF_CORE_FIELD_LSHIFT_U64 = 4, /* bitfield-specific left bitshift */ BPF_CORE_FIELD_RSHIFT_U64 = 5, /* bitfield-specific right bitshift */ BPF_CORE_TYPE_ID_LOCAL = 6, /* type ID in local BPF object */ BPF_CORE_TYPE_ID_TARGET = 7, /* type ID in target kernel */ BPF_CORE_TYPE_EXISTS = 8, /* type existence in target kernel */ BPF_CORE_TYPE_SIZE = 9, /* type size in bytes */ BPF_CORE_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */ BPF_CORE_ENUMVAL_VALUE = 11, /* enum value integer value */ BPF_CORE_TYPE_MATCHES = 12, /* type match in target kernel */ }; /* * "struct bpf_core_relo" is used to pass relocation data form LLVM to libbpf * and from libbpf to the kernel. * * CO-RE relocation captures the following data: * - insn_off - instruction offset (in bytes) within a BPF program that needs * its insn->imm field to be relocated with actual field info; * - type_id - BTF type ID of the "root" (containing) entity of a relocatable * type or field; * - access_str_off - offset into corresponding .BTF string section. String * interpretation depends on specific relocation kind: * - for field-based relocations, string encodes an accessed field using * a sequence of field and array indices, separated by colon (:). It's * conceptually very close to LLVM's getelementptr ([0]) instruction's * arguments for identifying offset to a field. * - for type-based relocations, strings is expected to be just "0"; * - for enum value-based relocations, string contains an index of enum * value within its enum type; * - kind - one of enum bpf_core_relo_kind; * * Example: * struct sample { * int a; * struct { * int b[10]; * }; * }; * * struct sample *s = ...; * int *x = &s->a; // encoded as "0:0" (a is field #0) * int *y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1, * // b is field #0 inside anon struct, accessing elem #5) * int *z = &s[10]->b; // encoded as "10:1" (ptr is used as an array) * * type_id for all relocs in this example will capture BTF type id of * `struct sample`. * * Such relocation is emitted when using __builtin_preserve_access_index() * Clang built-in, passing expression that captures field address, e.g.: * * bpf_probe_read(&dst, sizeof(dst), * __builtin_preserve_access_index(&src->a.b.c)); * * In this case Clang will emit field relocation recording necessary data to * be able to find offset of embedded `a.b.c` field within `src` struct. * * [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction */ struct bpf_core_relo { __u32 insn_off; __u32 type_id; __u32 access_str_off; enum bpf_core_relo_kind kind; }; /* * Flags to control bpf_timer_start() behaviour. * - BPF_F_TIMER_ABS: Timeout passed is absolute time, by default it is * relative to current time. * - BPF_F_TIMER_CPU_PIN: Timer will be pinned to the CPU of the caller. */ enum { BPF_F_TIMER_ABS = (1ULL << 0), BPF_F_TIMER_CPU_PIN = (1ULL << 1), }; /* BPF numbers iterator state */ struct bpf_iter_num { /* opaque iterator state; having __u64 here allows to preserve correct * alignment requirements in vmlinux.h, generated from BTF */ __u64 __opaque[1]; } __attribute__((aligned(8))); /* * Flags to control BPF kfunc behaviour. * - BPF_F_PAD_ZEROS: Pad destination buffer with zeros. (See the respective * helper documentation for details.) */ enum bpf_kfunc_flags { BPF_F_PAD_ZEROS = (1ULL << 0), }; #endif /* _UAPI__LINUX_BPF_H__ */ bpftrace-0.24.1/src/stdlib/include/linux/bpf_common.h000066400000000000000000000025461506776124200225270ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI__LINUX_BPF_COMMON_H__ #define _UAPI__LINUX_BPF_COMMON_H__ /* Instruction classes */ #define BPF_CLASS(code) ((code) & 0x07) #define BPF_LD 0x00 #define BPF_LDX 0x01 #define BPF_ST 0x02 #define BPF_STX 0x03 #define BPF_ALU 0x04 #define BPF_JMP 0x05 #define BPF_RET 0x06 #define BPF_MISC 0x07 /* ld/ldx fields */ #define BPF_SIZE(code) ((code) & 0x18) #define BPF_W 0x00 /* 32-bit */ #define BPF_H 0x08 /* 16-bit */ #define BPF_B 0x10 /* 8-bit */ /* eBPF BPF_DW 0x18 64-bit */ #define BPF_MODE(code) ((code) & 0xe0) #define BPF_IMM 0x00 #define BPF_ABS 0x20 #define BPF_IND 0x40 #define BPF_MEM 0x60 #define BPF_LEN 0x80 #define BPF_MSH 0xa0 /* alu/jmp fields */ #define BPF_OP(code) ((code) & 0xf0) #define BPF_ADD 0x00 #define BPF_SUB 0x10 #define BPF_MUL 0x20 #define BPF_DIV 0x30 #define BPF_OR 0x40 #define BPF_AND 0x50 #define BPF_LSH 0x60 #define BPF_RSH 0x70 #define BPF_NEG 0x80 #define BPF_MOD 0x90 #define BPF_XOR 0xa0 #define BPF_JA 0x00 #define BPF_JEQ 0x10 #define BPF_JGT 0x20 #define BPF_JGE 0x30 #define BPF_JSET 0x40 #define BPF_SRC(code) ((code) & 0x08) #define BPF_K 0x00 #define BPF_X 0x08 #ifndef BPF_MAXINSNS #define BPF_MAXINSNS 4096 #endif #endif /* _UAPI__LINUX_BPF_COMMON_H__ */ bpftrace-0.24.1/src/stdlib/include/linux/btf.h000066400000000000000000000130121506776124200211510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* Copyright (c) 2018 Facebook */ #ifndef _UAPI__LINUX_BTF_H__ #define _UAPI__LINUX_BTF_H__ #include #define BTF_MAGIC 0xeB9F #define BTF_VERSION 1 struct btf_header { __u16 magic; __u8 version; __u8 flags; __u32 hdr_len; /* All offsets are in bytes relative to the end of this header */ __u32 type_off; /* offset of type section */ __u32 type_len; /* length of type section */ __u32 str_off; /* offset of string section */ __u32 str_len; /* length of string section */ }; /* Max # of type identifier */ #define BTF_MAX_TYPE 0x000fffff /* Max offset into the string section */ #define BTF_MAX_NAME_OFFSET 0x00ffffff /* Max # of struct/union/enum members or func args */ #define BTF_MAX_VLEN 0xffff struct btf_type { __u32 name_off; /* "info" bits arrangement * bits 0-15: vlen (e.g. # of struct's members) * bits 16-23: unused * bits 24-28: kind (e.g. int, ptr, array...etc) * bits 29-30: unused * bit 31: kind_flag, currently used by * struct, union, enum, fwd, enum64, * decl_tag and type_tag */ __u32 info; /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64. * "size" tells the size of the type it is describing. * * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, * FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG. * "type" is a type_id referring to another type. */ union { __u32 size; __u32 type; }; }; #define BTF_INFO_KIND(info) (((info) >> 24) & 0x1f) #define BTF_INFO_VLEN(info) ((info) & 0xffff) #define BTF_INFO_KFLAG(info) ((info) >> 31) enum { BTF_KIND_UNKN = 0, /* Unknown */ BTF_KIND_INT = 1, /* Integer */ BTF_KIND_PTR = 2, /* Pointer */ BTF_KIND_ARRAY = 3, /* Array */ BTF_KIND_STRUCT = 4, /* Struct */ BTF_KIND_UNION = 5, /* Union */ BTF_KIND_ENUM = 6, /* Enumeration up to 32-bit values */ BTF_KIND_FWD = 7, /* Forward */ BTF_KIND_TYPEDEF = 8, /* Typedef */ BTF_KIND_VOLATILE = 9, /* Volatile */ BTF_KIND_CONST = 10, /* Const */ BTF_KIND_RESTRICT = 11, /* Restrict */ BTF_KIND_FUNC = 12, /* Function */ BTF_KIND_FUNC_PROTO = 13, /* Function Proto */ BTF_KIND_VAR = 14, /* Variable */ BTF_KIND_DATASEC = 15, /* Section */ BTF_KIND_FLOAT = 16, /* Floating point */ BTF_KIND_DECL_TAG = 17, /* Decl Tag */ BTF_KIND_TYPE_TAG = 18, /* Type Tag */ BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */ NR_BTF_KINDS, BTF_KIND_MAX = NR_BTF_KINDS - 1, }; /* For some specific BTF_KIND, "struct btf_type" is immediately * followed by extra data. */ /* BTF_KIND_INT is followed by a u32 and the following * is the 32 bits arrangement: */ #define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24) #define BTF_INT_OFFSET(VAL) (((VAL) & 0x00ff0000) >> 16) #define BTF_INT_BITS(VAL) ((VAL) & 0x000000ff) /* Attributes stored in the BTF_INT_ENCODING */ #define BTF_INT_SIGNED (1 << 0) #define BTF_INT_CHAR (1 << 1) #define BTF_INT_BOOL (1 << 2) /* BTF_KIND_ENUM is followed by multiple "struct btf_enum". * The exact number of btf_enum is stored in the vlen (of the * info in "struct btf_type"). */ struct btf_enum { __u32 name_off; __s32 val; }; /* BTF_KIND_ARRAY is followed by one "struct btf_array" */ struct btf_array { __u32 type; __u32 index_type; __u32 nelems; }; /* BTF_KIND_STRUCT and BTF_KIND_UNION are followed * by multiple "struct btf_member". The exact number * of btf_member is stored in the vlen (of the info in * "struct btf_type"). */ struct btf_member { __u32 name_off; __u32 type; /* If the type info kind_flag is set, the btf_member offset * contains both member bitfield size and bit offset. The * bitfield size is set for bitfield members. If the type * info kind_flag is not set, the offset contains only bit * offset. */ __u32 offset; }; /* If the struct/union type info kind_flag is set, the * following two macros are used to access bitfield_size * and bit_offset from btf_member.offset. */ #define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24) #define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff) /* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param". * The exact number of btf_param is stored in the vlen (of the * info in "struct btf_type"). */ struct btf_param { __u32 name_off; __u32 type; }; enum { BTF_VAR_STATIC = 0, BTF_VAR_GLOBAL_ALLOCATED = 1, BTF_VAR_GLOBAL_EXTERN = 2, }; enum btf_func_linkage { BTF_FUNC_STATIC = 0, BTF_FUNC_GLOBAL = 1, BTF_FUNC_EXTERN = 2, }; /* BTF_KIND_VAR is followed by a single "struct btf_var" to describe * additional information related to the variable such as its linkage. */ struct btf_var { __u32 linkage; }; /* BTF_KIND_DATASEC is followed by multiple "struct btf_var_secinfo" * to describe all BTF_KIND_VAR types it contains along with it's * in-section offset as well as size. */ struct btf_var_secinfo { __u32 type; __u32 offset; __u32 size; }; /* BTF_KIND_DECL_TAG is followed by a single "struct btf_decl_tag" to describe * additional information related to the tag applied location. * If component_idx == -1, the tag is applied to a struct, union, * variable or function. Otherwise, it is applied to a struct/union * member or a func argument, and component_idx indicates which member * or argument (0 ... vlen-1). */ struct btf_decl_tag { __s32 component_idx; }; /* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64". * The exact number of btf_enum64 is stored in the vlen (of the * info in "struct btf_type"). */ struct btf_enum64 { __u32 name_off; __u32 val_lo32; __u32 val_hi32; }; #endif /* _UAPI__LINUX_BTF_H__ */ bpftrace-0.24.1/src/stdlib/include/linux/byteorder/000077500000000000000000000000001506776124200222275ustar00rootroot00000000000000bpftrace-0.24.1/src/stdlib/include/linux/byteorder/big_endian.h000066400000000000000000000074471506776124200244730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_LINUX_BYTEORDER_BIG_ENDIAN_H #define _UAPI_LINUX_BYTEORDER_BIG_ENDIAN_H #ifndef __BIG_ENDIAN #define __BIG_ENDIAN 4321 #endif #ifndef __BIG_ENDIAN_BITFIELD #define __BIG_ENDIAN_BITFIELD #endif #include #include #include #define __constant_htonl(x) ((__force __be32)(__u32)(x)) #define __constant_ntohl(x) ((__force __u32)(__be32)(x)) #define __constant_htons(x) ((__force __be16)(__u16)(x)) #define __constant_ntohs(x) ((__force __u16)(__be16)(x)) #define __constant_cpu_to_le64(x) ((__force __le64)___constant_swab64((x))) #define __constant_le64_to_cpu(x) ___constant_swab64((__force __u64)(__le64)(x)) #define __constant_cpu_to_le32(x) ((__force __le32)___constant_swab32((x))) #define __constant_le32_to_cpu(x) ___constant_swab32((__force __u32)(__le32)(x)) #define __constant_cpu_to_le16(x) ((__force __le16)___constant_swab16((x))) #define __constant_le16_to_cpu(x) ___constant_swab16((__force __u16)(__le16)(x)) #define __constant_cpu_to_be64(x) ((__force __be64)(__u64)(x)) #define __constant_be64_to_cpu(x) ((__force __u64)(__be64)(x)) #define __constant_cpu_to_be32(x) ((__force __be32)(__u32)(x)) #define __constant_be32_to_cpu(x) ((__force __u32)(__be32)(x)) #define __constant_cpu_to_be16(x) ((__force __be16)(__u16)(x)) #define __constant_be16_to_cpu(x) ((__force __u16)(__be16)(x)) #define __cpu_to_le64(x) ((__force __le64)__swab64((x))) #define __le64_to_cpu(x) __swab64((__force __u64)(__le64)(x)) #define __cpu_to_le32(x) ((__force __le32)__swab32((x))) #define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x)) #define __cpu_to_le16(x) ((__force __le16)__swab16((x))) #define __le16_to_cpu(x) __swab16((__force __u16)(__le16)(x)) #define __cpu_to_be64(x) ((__force __be64)(__u64)(x)) #define __be64_to_cpu(x) ((__force __u64)(__be64)(x)) #define __cpu_to_be32(x) ((__force __be32)(__u32)(x)) #define __be32_to_cpu(x) ((__force __u32)(__be32)(x)) #define __cpu_to_be16(x) ((__force __be16)(__u16)(x)) #define __be16_to_cpu(x) ((__force __u16)(__be16)(x)) static __always_inline __le64 __cpu_to_le64p(const __u64 *p) { return (__force __le64)__swab64p(p); } static __always_inline __u64 __le64_to_cpup(const __le64 *p) { return __swab64p((__u64 *)p); } static __always_inline __le32 __cpu_to_le32p(const __u32 *p) { return (__force __le32)__swab32p(p); } static __always_inline __u32 __le32_to_cpup(const __le32 *p) { return __swab32p((__u32 *)p); } static __always_inline __le16 __cpu_to_le16p(const __u16 *p) { return (__force __le16)__swab16p(p); } static __always_inline __u16 __le16_to_cpup(const __le16 *p) { return __swab16p((__u16 *)p); } static __always_inline __be64 __cpu_to_be64p(const __u64 *p) { return (__force __be64)*p; } static __always_inline __u64 __be64_to_cpup(const __be64 *p) { return (__force __u64)*p; } static __always_inline __be32 __cpu_to_be32p(const __u32 *p) { return (__force __be32)*p; } static __always_inline __u32 __be32_to_cpup(const __be32 *p) { return (__force __u32)*p; } static __always_inline __be16 __cpu_to_be16p(const __u16 *p) { return (__force __be16)*p; } static __always_inline __u16 __be16_to_cpup(const __be16 *p) { return (__force __u16)*p; } #define __cpu_to_le64s(x) __swab64s((x)) #define __le64_to_cpus(x) __swab64s((x)) #define __cpu_to_le32s(x) __swab32s((x)) #define __le32_to_cpus(x) __swab32s((x)) #define __cpu_to_le16s(x) __swab16s((x)) #define __le16_to_cpus(x) __swab16s((x)) #define __cpu_to_be64s(x) do { (void)(x); } while (0) #define __be64_to_cpus(x) do { (void)(x); } while (0) #define __cpu_to_be32s(x) do { (void)(x); } while (0) #define __be32_to_cpus(x) do { (void)(x); } while (0) #define __cpu_to_be16s(x) do { (void)(x); } while (0) #define __be16_to_cpus(x) do { (void)(x); } while (0) #endif /* _UAPI_LINUX_BYTEORDER_BIG_ENDIAN_H */ bpftrace-0.24.1/src/stdlib/include/linux/byteorder/little_endian.h000066400000000000000000000075541506776124200252260ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_LINUX_BYTEORDER_LITTLE_ENDIAN_H #define _UAPI_LINUX_BYTEORDER_LITTLE_ENDIAN_H #ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN 1234 #endif #ifndef __LITTLE_ENDIAN_BITFIELD #define __LITTLE_ENDIAN_BITFIELD #endif #include #include #include #define __constant_htonl(x) ((__force __be32)___constant_swab32((x))) #define __constant_ntohl(x) ___constant_swab32((__force __be32)(x)) #define __constant_htons(x) ((__force __be16)___constant_swab16((x))) #define __constant_ntohs(x) ___constant_swab16((__force __be16)(x)) #define __constant_cpu_to_le64(x) ((__force __le64)(__u64)(x)) #define __constant_le64_to_cpu(x) ((__force __u64)(__le64)(x)) #define __constant_cpu_to_le32(x) ((__force __le32)(__u32)(x)) #define __constant_le32_to_cpu(x) ((__force __u32)(__le32)(x)) #define __constant_cpu_to_le16(x) ((__force __le16)(__u16)(x)) #define __constant_le16_to_cpu(x) ((__force __u16)(__le16)(x)) #define __constant_cpu_to_be64(x) ((__force __be64)___constant_swab64((x))) #define __constant_be64_to_cpu(x) ___constant_swab64((__force __u64)(__be64)(x)) #define __constant_cpu_to_be32(x) ((__force __be32)___constant_swab32((x))) #define __constant_be32_to_cpu(x) ___constant_swab32((__force __u32)(__be32)(x)) #define __constant_cpu_to_be16(x) ((__force __be16)___constant_swab16((x))) #define __constant_be16_to_cpu(x) ___constant_swab16((__force __u16)(__be16)(x)) #define __cpu_to_le64(x) ((__force __le64)(__u64)(x)) #define __le64_to_cpu(x) ((__force __u64)(__le64)(x)) #define __cpu_to_le32(x) ((__force __le32)(__u32)(x)) #define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) #define __cpu_to_le16(x) ((__force __le16)(__u16)(x)) #define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) #define __cpu_to_be64(x) ((__force __be64)__swab64((x))) #define __be64_to_cpu(x) __swab64((__force __u64)(__be64)(x)) #define __cpu_to_be32(x) ((__force __be32)__swab32((x))) #define __be32_to_cpu(x) __swab32((__force __u32)(__be32)(x)) #define __cpu_to_be16(x) ((__force __be16)__swab16((x))) #define __be16_to_cpu(x) __swab16((__force __u16)(__be16)(x)) static __always_inline __le64 __cpu_to_le64p(const __u64 *p) { return (__force __le64)*p; } static __always_inline __u64 __le64_to_cpup(const __le64 *p) { return (__force __u64)*p; } static __always_inline __le32 __cpu_to_le32p(const __u32 *p) { return (__force __le32)*p; } static __always_inline __u32 __le32_to_cpup(const __le32 *p) { return (__force __u32)*p; } static __always_inline __le16 __cpu_to_le16p(const __u16 *p) { return (__force __le16)*p; } static __always_inline __u16 __le16_to_cpup(const __le16 *p) { return (__force __u16)*p; } static __always_inline __be64 __cpu_to_be64p(const __u64 *p) { return (__force __be64)__swab64p(p); } static __always_inline __u64 __be64_to_cpup(const __be64 *p) { return __swab64p((__u64 *)p); } static __always_inline __be32 __cpu_to_be32p(const __u32 *p) { return (__force __be32)__swab32p(p); } static __always_inline __u32 __be32_to_cpup(const __be32 *p) { return __swab32p((__u32 *)p); } static __always_inline __be16 __cpu_to_be16p(const __u16 *p) { return (__force __be16)__swab16p(p); } static __always_inline __u16 __be16_to_cpup(const __be16 *p) { return __swab16p((__u16 *)p); } #define __cpu_to_le64s(x) do { (void)(x); } while (0) #define __le64_to_cpus(x) do { (void)(x); } while (0) #define __cpu_to_le32s(x) do { (void)(x); } while (0) #define __le32_to_cpus(x) do { (void)(x); } while (0) #define __cpu_to_le16s(x) do { (void)(x); } while (0) #define __le16_to_cpus(x) do { (void)(x); } while (0) #define __cpu_to_be64s(x) __swab64s((x)) #define __be64_to_cpus(x) __swab64s((x)) #define __cpu_to_be32s(x) __swab32s((x)) #define __be32_to_cpus(x) __swab32s((x)) #define __cpu_to_be16s(x) __swab16s((x)) #define __be16_to_cpus(x) __swab16s((x)) #endif /* _UAPI_LINUX_BYTEORDER_LITTLE_ENDIAN_H */ bpftrace-0.24.1/src/stdlib/include/linux/compiler-clang.h000066400000000000000000000106011506776124200232730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __LINUX_COMPILER_TYPES_H #error "Please do not include directly, include instead." #endif /* Compiler specific definitions for Clang compiler */ /* * Clang prior to 17 is being silly and considers many __cleanup() variables * as unused (because they are, their sole purpose is to go out of scope). * * https://github.com/llvm/llvm-project/commit/877210faa447f4cc7db87812f8ed80e398fedd61 */ #undef __cleanup #define __cleanup(func) __maybe_unused __attribute__((__cleanup__(func))) /* all clang versions usable with the kernel support KASAN ABI version 5 */ #define KASAN_ABI_VERSION 5 /* * Note: Checking __has_feature(*_sanitizer) is only true if the feature is * enabled. Therefore it is not required to additionally check defined(CONFIG_*) * to avoid adding redundant attributes in other configurations. */ #if __has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer) /* Emulate GCC's __SANITIZE_ADDRESS__ flag */ #define __SANITIZE_ADDRESS__ #define __no_sanitize_address \ __attribute__((no_sanitize("address", "hwaddress"))) #else #define __no_sanitize_address #endif #if __has_feature(thread_sanitizer) /* emulate gcc's __SANITIZE_THREAD__ flag */ #define __SANITIZE_THREAD__ #define __no_sanitize_thread \ __attribute__((no_sanitize("thread"))) #else #define __no_sanitize_thread #endif #if defined(CONFIG_ARCH_USE_BUILTIN_BSWAP) #define __HAVE_BUILTIN_BSWAP32__ #define __HAVE_BUILTIN_BSWAP64__ #define __HAVE_BUILTIN_BSWAP16__ #endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ #if __has_feature(undefined_behavior_sanitizer) /* GCC does not have __SANITIZE_UNDEFINED__ */ #define __no_sanitize_undefined \ __attribute__((no_sanitize("undefined"))) #else #define __no_sanitize_undefined #endif #if __has_feature(memory_sanitizer) #define __SANITIZE_MEMORY__ /* * Unlike other sanitizers, KMSAN still inserts code into functions marked with * no_sanitize("kernel-memory"). Using disable_sanitizer_instrumentation * provides the behavior consistent with other __no_sanitize_ attributes, * guaranteeing that __no_sanitize_memory functions remain uninstrumented. */ #define __no_sanitize_memory __disable_sanitizer_instrumentation /* * The __no_kmsan_checks attribute ensures that a function does not produce * false positive reports by: * - initializing all local variables and memory stores in this function; * - skipping all shadow checks; * - passing initialized arguments to this function's callees. */ #define __no_kmsan_checks __attribute__((no_sanitize("kernel-memory"))) #else #define __no_sanitize_memory #define __no_kmsan_checks #endif /* * Support for __has_feature(coverage_sanitizer) was added in Clang 13 together * with no_sanitize("coverage"). Prior versions of Clang support coverage * instrumentation, but cannot be queried for support by the preprocessor. */ #if __has_feature(coverage_sanitizer) #define __no_sanitize_coverage __attribute__((no_sanitize("coverage"))) #else #define __no_sanitize_coverage #endif #if __has_feature(shadow_call_stack) # define __noscs __attribute__((__no_sanitize__("shadow-call-stack"))) #endif #if __has_feature(kcfi) /* Disable CFI checking inside a function. */ #define __nocfi __attribute__((__no_sanitize__("kcfi"))) #endif /* * Turn individual warnings and errors on and off locally, depending * on version. */ #define __diag_clang(version, severity, s) \ __diag_clang_ ## version(__diag_clang_ ## severity s) /* Severity used in pragma directives */ #define __diag_clang_ignore ignored #define __diag_clang_warn warning #define __diag_clang_error error #define __diag_str1(s) #s #define __diag_str(s) __diag_str1(s) #define __diag(s) _Pragma(__diag_str(clang diagnostic s)) #define __diag_clang_13(s) __diag(s) #define __diag_ignore_all(option, comment) \ __diag_clang(13, ignore, option) /* * clang has horrible behavior with "g" or "rm" constraints for asm * inputs, turning them into something worse than "m". Avoid using * constraints with multiple possible uses (but "ir" seems to be ok): * * https://github.com/llvm/llvm-project/issues/20571 */ #define ASM_INPUT_G "ir" #define ASM_INPUT_RM "r" /* * Declare compiler support for __typeof_unqual__() operator. * * Bindgen uses LLVM even if our C compiler is GCC, so we cannot * rely on the auto-detected CONFIG_CC_HAS_TYPEOF_UNQUAL. */ #define CC_HAS_TYPEOF_UNQUAL (__clang_major__ >= 19) bpftrace-0.24.1/src/stdlib/include/linux/compiler.h000066400000000000000000000330241506776124200222150ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __LINUX_COMPILER_H #define __LINUX_COMPILER_H #include #ifndef __ASSEMBLY__ #ifdef __KERNEL__ /* * Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code * to disable branch tracing on a per file basis. */ void ftrace_likely_update(struct ftrace_likely_data *f, int val, int expect, int is_constant); #if defined(CONFIG_TRACE_BRANCH_PROFILING) \ && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__) #define likely_notrace(x) __builtin_expect(!!(x), 1) #define unlikely_notrace(x) __builtin_expect(!!(x), 0) #define __branch_check__(x, expect, is_constant) ({ \ long ______r; \ static struct ftrace_likely_data \ __aligned(4) \ __section("_ftrace_annotated_branch") \ ______f = { \ .data.func = __func__, \ .data.file = __FILE__, \ .data.line = __LINE__, \ }; \ ______r = __builtin_expect(!!(x), expect); \ ftrace_likely_update(&______f, ______r, \ expect, is_constant); \ ______r; \ }) /* * Using __builtin_constant_p(x) to ignore cases where the return * value is always the same. This idea is taken from a similar patch * written by Daniel Walker. */ # ifndef likely # define likely(x) (__branch_check__(x, 1, __builtin_constant_p(x))) # endif # ifndef unlikely # define unlikely(x) (__branch_check__(x, 0, __builtin_constant_p(x))) # endif #ifdef CONFIG_PROFILE_ALL_BRANCHES /* * "Define 'is'", Bill Clinton * "Define 'if'", Steven Rostedt */ #define if(cond, ...) if ( __trace_if_var( !!(cond , ## __VA_ARGS__) ) ) #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond)) #define __trace_if_value(cond) ({ \ static struct ftrace_branch_data \ __aligned(4) \ __section("_ftrace_branch") \ __if_trace = { \ .func = __func__, \ .file = __FILE__, \ .line = __LINE__, \ }; \ (cond) ? \ (__if_trace.miss_hit[1]++,1) : \ (__if_trace.miss_hit[0]++,0); \ }) #endif /* CONFIG_PROFILE_ALL_BRANCHES */ #else # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) # define likely_notrace(x) likely(x) # define unlikely_notrace(x) unlikely(x) #endif /* Optimization barrier */ #ifndef barrier /* The "volatile" is due to gcc bugs */ # define barrier() __asm__ __volatile__("": : :"memory") #endif #ifndef barrier_data /* * This version is i.e. to prevent dead stores elimination on @ptr * where gcc and llvm may behave differently when otherwise using * normal barrier(): while gcc behavior gets along with a normal * barrier(), llvm needs an explicit input variable to be assumed * clobbered. The issue is as follows: while the inline asm might * access any memory it wants, the compiler could have fit all of * @ptr into memory registers instead, and since @ptr never escaped * from that, it proved that the inline asm wasn't touching any of * it. This version works well with both compilers, i.e. we're telling * the compiler that the inline asm absolutely may see the contents * of @ptr. See also: https://llvm.org/bugs/show_bug.cgi?id=15495 */ # define barrier_data(ptr) __asm__ __volatile__("": :"r"(ptr) :"memory") #endif /* workaround for GCC PR82365 if needed */ #ifndef barrier_before_unreachable # define barrier_before_unreachable() do { } while (0) #endif /* Unreachable code */ #ifdef CONFIG_OBJTOOL /* Annotate a C jump table to allow objtool to follow the code flow */ #define __annotate_jump_table __section(".data.rel.ro.c_jump_table") #else /* !CONFIG_OBJTOOL */ #define __annotate_jump_table #endif /* CONFIG_OBJTOOL */ /* * Mark a position in code as unreachable. This can be used to * suppress control flow warnings after asm blocks that transfer * control elsewhere. */ #define unreachable() do { \ barrier_before_unreachable(); \ __builtin_unreachable(); \ } while (0) /* * KENTRY - kernel entry point * This can be used to annotate symbols (functions or data) that are used * without their linker symbol being referenced explicitly. For example, * interrupt vector handlers, or functions in the kernel image that are found * programatically. * * Not required for symbols exported with EXPORT_SYMBOL, or initcalls. Those * are handled in their own way (with KEEP() in linker scripts). * * KENTRY can be avoided if the symbols in question are marked as KEEP() in the * linker script. For example an architecture could KEEP() its entire * boot/exception vector code rather than annotate each function and data. */ #ifndef KENTRY # define KENTRY(sym) \ extern typeof(sym) sym; \ static const unsigned long __kentry_##sym \ __used \ __attribute__((__section__("___kentry+" #sym))) \ = (unsigned long)&sym; #endif #ifndef RELOC_HIDE # define RELOC_HIDE(ptr, off) \ ({ unsigned long __ptr; \ __ptr = (unsigned long) (ptr); \ (typeof(ptr)) (__ptr + (off)); }) #endif #define absolute_pointer(val) RELOC_HIDE((void *)(val), 0) #ifndef OPTIMIZER_HIDE_VAR /* Make the optimizer believe the variable can be manipulated arbitrarily. */ #define OPTIMIZER_HIDE_VAR(var) \ __asm__ ("" : "=r" (var) : "0" (var)) #endif #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) /** * data_race - mark an expression as containing intentional data races * * This data_race() macro is useful for situations in which data races * should be forgiven. One example is diagnostic code that accesses * shared variables but is not a part of the core synchronization design. * For example, if accesses to a given variable are protected by a lock, * except for diagnostic code, then the accesses under the lock should * be plain C-language accesses and those in the diagnostic code should * use data_race(). This way, KCSAN will complain if buggy lockless * accesses to that variable are introduced, even if the buggy accesses * are protected by READ_ONCE() or WRITE_ONCE(). * * This macro *does not* affect normal code generation, but is a hint * to tooling that data races here are to be ignored. If the access must * be atomic *and* KCSAN should ignore the access, use both data_race() * and READ_ONCE(), for example, data_race(READ_ONCE(x)). */ #define data_race(expr) \ ({ \ __kcsan_disable_current(); \ __auto_type __v = (expr); \ __kcsan_enable_current(); \ __v; \ }) #ifdef __CHECKER__ #define __BUILD_BUG_ON_ZERO_MSG(e, msg, ...) (0) #else /* __CHECKER__ */ #define __BUILD_BUG_ON_ZERO_MSG(e, msg, ...) ((int)sizeof(struct {_Static_assert(!(e), msg);})) #endif /* __CHECKER__ */ /* &a[0] degrades to a pointer: a different type from an array */ #define __is_array(a) (!__same_type((a), &(a)[0])) #define __must_be_array(a) __BUILD_BUG_ON_ZERO_MSG(!__is_array(a), \ "must be array") #define __is_byte_array(a) (__is_array(a) && sizeof((a)[0]) == 1) #define __must_be_byte_array(a) __BUILD_BUG_ON_ZERO_MSG(!__is_byte_array(a), \ "must be byte array") /* * If the "nonstring" attribute isn't available, we have to return true * so the __must_*() checks pass when "nonstring" isn't supported. */ #if __has_attribute(__nonstring__) && defined(__annotated) #define __is_cstr(a) (!__annotated(a, nonstring)) #define __is_noncstr(a) (__annotated(a, nonstring)) #else #define __is_cstr(a) (true) #define __is_noncstr(a) (true) #endif /* Require C Strings (i.e. NUL-terminated) lack the "nonstring" attribute. */ #define __must_be_cstr(p) \ __BUILD_BUG_ON_ZERO_MSG(!__is_cstr(p), \ "must be C-string (NUL-terminated)") #define __must_be_noncstr(p) \ __BUILD_BUG_ON_ZERO_MSG(!__is_noncstr(p), \ "must be non-C-string (not NUL-terminated)") /* * Use __typeof_unqual__() when available. * * XXX: Remove test for __CHECKER__ once * sparse learns about __typeof_unqual__(). */ #if CC_HAS_TYPEOF_UNQUAL && !defined(__CHECKER__) # define USE_TYPEOF_UNQUAL 1 #endif /* * Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof * operator when available, to return an unqualified type of the exp. */ #if defined(USE_TYPEOF_UNQUAL) # define TYPEOF_UNQUAL(exp) __typeof_unqual__(exp) #else # define TYPEOF_UNQUAL(exp) __typeof__(exp) #endif #endif /* __KERNEL__ */ #if defined(CONFIG_CFI_CLANG) && !defined(__DISABLE_EXPORTS) && !defined(BUILD_VDSO) /* * Force a reference to the external symbol so the compiler generates * __kcfi_typid. */ #define KCFI_REFERENCE(sym) __ADDRESSABLE(sym) #else #define KCFI_REFERENCE(sym) #endif /** * offset_to_ptr - convert a relative memory offset to an absolute pointer * @off: the address of the 32-bit offset value */ static inline void *offset_to_ptr(const int *off) { return (void *)((unsigned long)off + *off); } #endif /* __ASSEMBLY__ */ #ifdef CONFIG_64BIT #define ARCH_SEL(a,b) a #else #define ARCH_SEL(a,b) b #endif /* * Force the compiler to emit 'sym' as a symbol, so that we can reference * it from inline assembler. Necessary in case 'sym' could be inlined * otherwise, or eliminated entirely due to lack of references that are * visible to the compiler. */ #define ___ADDRESSABLE(sym, __attrs) \ static void * __used __attrs \ __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym; #define __ADDRESSABLE(sym) \ ___ADDRESSABLE(sym, __section(".discard.addressable")) #define __ADDRESSABLE_ASM(sym) \ .pushsection .discard.addressable,"aw"; \ .align ARCH_SEL(8,4); \ ARCH_SEL(.quad, .long) __stringify(sym); \ .popsection; #define __ADDRESSABLE_ASM_STR(sym) __stringify(__ADDRESSABLE_ASM(sym)) /* * This returns a constant expression while determining if an argument is * a constant expression, most importantly without evaluating the argument. * Glory to Martin Uecker * * Details: * - sizeof() return an integer constant expression, and does not evaluate * the value of its operand; it only examines the type of its operand. * - The results of comparing two integer constant expressions is also * an integer constant expression. * - The first literal "8" isn't important. It could be any literal value. * - The second literal "8" is to avoid warnings about unaligned pointers; * this could otherwise just be "1". * - (long)(x) is used to avoid warnings about 64-bit types on 32-bit * architectures. * - The C Standard defines "null pointer constant", "(void *)0", as * distinct from other void pointers. * - If (x) is an integer constant expression, then the "* 0l" resolves * it into an integer constant expression of value 0. Since it is cast to * "void *", this makes the second operand a null pointer constant. * - If (x) is not an integer constant expression, then the second operand * resolves to a void pointer (but not a null pointer constant: the value * is not an integer constant 0). * - The conditional operator's third operand, "(int *)8", is an object * pointer (to type "int"). * - The behavior (including the return type) of the conditional operator * ("operand1 ? operand2 : operand3") depends on the kind of expressions * given for the second and third operands. This is the central mechanism * of the macro: * - When one operand is a null pointer constant (i.e. when x is an integer * constant expression) and the other is an object pointer (i.e. our * third operand), the conditional operator returns the type of the * object pointer operand (i.e. "int *"). Here, within the sizeof(), we * would then get: * sizeof(*((int *)(...)) == sizeof(int) == 4 * - When one operand is a void pointer (i.e. when x is not an integer * constant expression) and the other is an object pointer (i.e. our * third operand), the conditional operator returns a "void *" type. * Here, within the sizeof(), we would then get: * sizeof(*((void *)(...)) == sizeof(void) == 1 * - The equality comparison to "sizeof(int)" therefore depends on (x): * sizeof(int) == sizeof(int) (x) was a constant expression * sizeof(int) != sizeof(void) (x) was not a constant expression */ #define __is_constexpr(x) \ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) /* * Whether 'type' is a signed type or an unsigned type. Supports scalar types, * bool and also pointer types. */ #define is_signed_type(type) (((type)(-1)) < (__force type)1) #define is_unsigned_type(type) (!is_signed_type(type)) /* * Useful shorthand for "is this condition known at compile-time?" * * Note that the condition may involve non-constant values, * but the compiler may know enough about the details of the * values to determine that the condition is statically true. */ #define statically_true(x) (__builtin_constant_p(x) && (x)) /* * Similar to statically_true() but produces a constant expression * * To be used in conjunction with macros, such as BUILD_BUG_ON_ZERO(), * which require their input to be a constant expression and for which * statically_true() would otherwise fail. * * This is a trade-off: const_true() requires all its operands to be * compile time constants. Else, it would always returns false even on * the most trivial cases like: * * true || non_const_var * * On the opposite, statically_true() is able to fold more complex * tautologies and will return true on expressions such as: * * !(non_const_var * 8 % 4) * * For the general case, statically_true() is better. */ #define const_true(x) __builtin_choose_expr(__is_constexpr(x), x, false) /* * This is needed in functions which generate the stack canary, see * arch/x86/kernel/smpboot.c::start_secondary() for an example. */ #define prevent_tail_call_optimization() mb() #include #endif /* __LINUX_COMPILER_H */ bpftrace-0.24.1/src/stdlib/include/linux/compiler_attributes.h000066400000000000000000000376471506776124200245020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __LINUX_COMPILER_ATTRIBUTES_H #define __LINUX_COMPILER_ATTRIBUTES_H /* * The attributes in this file are unconditionally defined and they directly * map to compiler attribute(s), unless one of the compilers does not support * the attribute. In that case, __has_attribute is used to check for support * and the reason is stated in its comment ("Optional: ..."). * * Any other "attributes" (i.e. those that depend on a configuration option, * on a compiler, on an architecture, on plugins, on other attributes...) * should be defined elsewhere (e.g. compiler_types.h or compiler-*.h). * The intention is to keep this file as simple as possible, as well as * compiler- and version-agnostic (e.g. avoiding GCC_VERSION checks). * * This file is meant to be sorted (by actual attribute name, * not by #define identifier). Use the __attribute__((__name__)) syntax * (i.e. with underscores) to avoid future collisions with other macros. * Provide links to the documentation of each supported compiler, if it exists. */ /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-alias-function-attribute */ #define __alias(symbol) __attribute__((__alias__(#symbol))) /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-aligned-function-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-aligned-type-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-aligned-variable-attribute */ #define __aligned(x) __attribute__((__aligned__(x))) #define __aligned_largest __attribute__((__aligned__)) /* * Note: do not use this directly. Instead, use __alloc_size() since it is conditionally * available and includes other attributes. For GCC < 9.1, __alloc_size__ gets undefined * in compiler-gcc.h, due to misbehaviors. * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-alloc_005fsize-function-attribute * clang: https://clang.llvm.org/docs/AttributeReference.html#alloc-size */ #define __alloc_size__(x, ...) __attribute__((__alloc_size__(x, ## __VA_ARGS__))) /* * Note: users of __always_inline currently do not write "inline" themselves, * which seems to be required by gcc to apply the attribute according * to its docs (and also "warning: always_inline function might not be * inlinable [-Wattributes]" is emitted). * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-always_005finline-function-attribute * clang: mentioned */ #define __always_inline inline __attribute__((__always_inline__)) /* * The second argument is optional (default 0), so we use a variadic macro * to make the shorthand. * * Beware: Do not apply this to functions which may return * ERR_PTRs. Also, it is probably unwise to apply it to functions * returning extra information in the low bits (but in that case the * compiler should see some alignment anyway, when the return value is * massaged by 'flags = ptr & 3; ptr &= ~3;'). * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-assume_005faligned-function-attribute * clang: https://clang.llvm.org/docs/AttributeReference.html#assume-aligned */ #define __assume_aligned(a, ...) __attribute__((__assume_aligned__(a, ## __VA_ARGS__))) /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-cleanup-variable-attribute * clang: https://clang.llvm.org/docs/AttributeReference.html#cleanup */ #define __cleanup(func) __attribute__((__cleanup__(func))) /* * Note the long name. * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute */ #define __attribute_const__ __attribute__((__const__)) /* * Optional: only supported since gcc >= 9 * Optional: not supported by clang * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-copy-function-attribute */ #if __has_attribute(__copy__) # define __copy(symbol) __attribute__((__copy__(symbol))) #else # define __copy(symbol) #endif /* * Optional: not supported by gcc * Optional: only supported since clang >= 14.0 * * clang: https://clang.llvm.org/docs/AttributeReference.html#diagnose_as_builtin */ #if __has_attribute(__diagnose_as_builtin__) # define __diagnose_as(builtin...) __attribute__((__diagnose_as_builtin__(builtin))) #else # define __diagnose_as(builtin...) #endif /* * Don't. Just don't. See commit 771c035372a0 ("deprecate the '__deprecated' * attribute warnings entirely and for good") for more information. * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-deprecated-function-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-deprecated-type-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-deprecated-variable-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html#index-deprecated-enumerator-attribute * clang: https://clang.llvm.org/docs/AttributeReference.html#deprecated */ #define __deprecated /* * Optional: not supported by clang * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-designated_005finit-type-attribute */ #if __has_attribute(__designated_init__) # define __designated_init __attribute__((__designated_init__)) #else # define __designated_init #endif /* * Optional: only supported since clang >= 14.0 * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-error-function-attribute */ #if __has_attribute(__error__) # define __compiletime_error(msg) __attribute__((__error__(msg))) #else # define __compiletime_error(msg) #endif /* * Optional: not supported by clang * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-externally_005fvisible-function-attribute */ #if __has_attribute(__externally_visible__) # define __visible __attribute__((__externally_visible__)) #else # define __visible #endif /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-format-function-attribute * clang: https://clang.llvm.org/docs/AttributeReference.html#format */ #define __printf(a, b) __attribute__((__format__(printf, a, b))) #define __scanf(a, b) __attribute__((__format__(scanf, a, b))) /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-gnu_005finline-function-attribute * clang: https://clang.llvm.org/docs/AttributeReference.html#gnu-inline */ #define __gnu_inline __attribute__((__gnu_inline__)) /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-malloc-function-attribute * clang: https://clang.llvm.org/docs/AttributeReference.html#malloc */ #define __malloc __attribute__((__malloc__)) /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-mode-type-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-mode-variable-attribute */ #define __mode(x) __attribute__((__mode__(x))) /* * Optional: only supported since gcc >= 7 * * gcc: https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html#index-no_005fcaller_005fsaved_005fregisters-function-attribute_002c-x86 * clang: https://clang.llvm.org/docs/AttributeReference.html#no-caller-saved-registers */ #if __has_attribute(__no_caller_saved_registers__) # define __no_caller_saved_registers __attribute__((__no_caller_saved_registers__)) #else # define __no_caller_saved_registers #endif /* * Optional: not supported by clang * * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noclone-function-attribute */ #if __has_attribute(__noclone__) # define __noclone __attribute__((__noclone__)) #else # define __noclone #endif /* * Add the pseudo keyword 'fallthrough' so case statement blocks * must end with any of these keywords: * break; * fallthrough; * continue; * goto