pax_global_header00006660000000000000000000000064141346050240014511gustar00rootroot0000000000000052 comment=3c5788eae8293c848c537bb6506751527ac59425 bpftrace-0.14.0/000077500000000000000000000000001413460502400133615ustar00rootroot00000000000000bpftrace-0.14.0/.clang-format000066400000000000000000000060141413460502400157350ustar00rootroot00000000000000--- 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 BraceWrapping: # AfterCaseLabel was added in clang9 #AfterCaseLabel: true AfterClass: true AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: false AfterStruct: true AfterUnion: true AfterExternBlock: true BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom 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 FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: true IndentPPDirectives: None IndentWidth: 2 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None # Note: These penalties are not only relative to each other, but also to # penalties for more cases hardcoded into clang-format PenaltyBreakAssignment: 100 PenaltyBreakBeforeFirstCallParameter: 75 PenaltyBreakComment: 1 PenaltyBreakFirstLessLess: 50 PenaltyBreakString: 500 PenaltyExcessCharacter: 1000 PenaltyReturnTypeOnItsOwnLine: 1000000 PointerAlignment: Right ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 8 UseTab: Never ... bpftrace-0.14.0/.editorconfig000066400000000000000000000003231413460502400160340ustar00rootroot00000000000000[*] 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.14.0/.gitattributes000066400000000000000000000000601413460502400162500ustar00rootroot00000000000000*.bt linguist-language=D *.bt linguist-vendored bpftrace-0.14.0/.github/000077500000000000000000000000001413460502400147215ustar00rootroot00000000000000bpftrace-0.14.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001413460502400171045ustar00rootroot00000000000000bpftrace-0.14.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000007061413460502400216010ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve labels: bug --- ### What reproduces the bug? ### bpftrace --info bpftrace-0.14.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002571413460502400211000ustar00rootroot00000000000000blank_issues_enabled: true contact_links: - name: Community Support url: https://github.com/iovisor/bpftrace/discussions about: Please ask and answer questions here bpftrace-0.14.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000006561413460502400226400ustar00rootroot00000000000000--- 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.14.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000005331413460502400205230ustar00rootroot00000000000000 ##### Checklist - [ ] Language changes are updated in `man/adoc/bpftrace.adoc` and if needed in `docs/reference_guide.md` - [ ] User-visible and non-trivial changes updated in `CHANGELOG.md` - [ ] The new behaviour is covered by tests bpftrace-0.14.0/.github/workflows/000077500000000000000000000000001413460502400167565ustar00rootroot00000000000000bpftrace-0.14.0/.github/workflows/ci.yml000066400000000000000000000170731413460502400201040ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: clang-format: # Only run clang-format on pull requests. We want to allow people to # ignore clang-format if they think it's not helpful. if: "github.event_name == 'pull_request'" runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install clang-format run: sudo apt-get install clang-format - name: Download git-clang-format run: wget https://raw.githubusercontent.com/llvm-mirror/clang/master/tools/clang-format/git-clang-format - name: Install git-clang-format run: sudo install -t /bin git-clang-format - name: Fetch origin master run: git fetch --no-tags --prune --depth=1 origin master - name: clang-format run: git clang-format origin/master - name: diff run: git diff --exit-code build_test: runs-on: ubuntu-latest strategy: matrix: env: - NAME: LLVM 6 Debug TYPE: Debug LLVM_VERSION: "6.0" RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 6 Release TYPE: Release LLVM_VERSION: "6.0" RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 7 Debug TYPE: Debug LLVM_VERSION: 7 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 7 Release TYPE: Release LLVM_VERSION: 7 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 8 Debug TYPE: Debug LLVM_VERSION: 8 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 8 Release TYPE: Release LLVM_VERSION: 8 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 9 Debug TYPE: Debug LLVM_VERSION: 9 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 9 Release TYPE: Release LLVM_VERSION: 9 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 10 Debug TYPE: Debug LLVM_VERSION: 10 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 10 Release TYPE: Release LLVM_VERSION: 10 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 10 Clang Debug TYPE: Debug LLVM_VERSION: 10 CC: clang-10 CXX: clang++-10 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 11 Debug TYPE: Debug LLVM_VERSION: 11 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 11 Release TYPE: Release LLVM_VERSION: 11 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 12 Release TYPE: Release LLVM_VERSION: 12 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 13 Release TYPE: Release LLVM_VERSION: 13 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size GTEST_FILTER: '-clang_parser.nested_struct_no_type' BASE: bionic VENDOR_GTEST: ON - NAME: LLVM 12 Release + libbpf TYPE: Release LLVM_VERSION: 12 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size BASE: bionic VENDOR_GTEST: ON BUILD_LIBBPF: ON - NAME: Memleak test (LLVM 11 Debug) TYPE: Debug LLVM_VERSION: 11 BASE: bionic RUN_MEMLEAK_TEST: 1 RUN_TESTS: 0 VENDOR_GTEST: ON - NAME: Memleak test (LLVM 11 Release) TYPE: Release LLVM_VERSION: 11 BASE: bionic RUN_MEMLEAK_TEST: 1 RUN_TESTS: 0 VENDOR_GTEST: ON steps: - uses: actions/checkout@v2 - name: Get date id: get-date run: echo "::set-output name=date::$(/bin/date -u "+%Y.week%g")" shell: bash - name: Cache docker image env: ${{matrix.env}} id: docker-cache uses: actions/cache@v2 with: path: /tmp/docker-save # Key the cache entry by: # * the operating system # * the week (so cache gets invalidated every week) # * the image configuration (ie llvm version & distro) # * the hash of all the files in docker/ key: ${{ runner.os }}-docker-cache-${{ steps.get-date.outputs.date }}-${{ env.NAME }}-${{ hashFiles('docker/**') }} - name: Build docker container if: steps.docker-cache.outputs.cache-hit != 'true' env: ${{matrix.env}} run: > docker build --build-arg LLVM_VERSION=$LLVM_VERSION -t bpftrace-builder-$BASE-llvm-$LLVM_VERSION -f docker/Dockerfile.$BASE docker/ && mkdir -p /tmp/docker-save && docker save bpftrace-builder-$BASE-llvm-$LLVM_VERSION -o /tmp/docker-save/i.tar - name: Load the cached docker image (if available) if: steps.docker-cache.outputs.cache-hit == 'true' run: > docker load --input /tmp/docker-save/i.tar - name: Build and test env: ${{matrix.env}} run: > docker run --privileged -v $(pwd):$(pwd) -w $(pwd) -v /sys/kernel/debug:/sys/kernel/debug:rw -v /lib/modules:/lib/modules:ro -v /usr/src:/usr/src:ro -e RUN_TESTS=${RUN_TESTS} -e RUN_ALL_TESTS=${RUN_ALL_TESTS} -e RUN_MEMLEAK_TEST="${RUN_MEMLEAK_TEST}" -e CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" -e RUNTIME_TEST_DISABLE="${RUNTIME_TEST_DISABLE}" -e VENDOR_GTEST="${VENDOR_GTEST}" -e BUILD_LIBBPF="${BUILD_LIBBPF}" -e CC="${CC}" -e CXX="${CXX}" -e GTEST_FILTER="$GTEST_FILTER" bpftrace-builder-$BASE-llvm-$LLVM_VERSION ${PWD}/build-$TYPE-$BASE $TYPE -j$(nproc) 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 == 'iovisor/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/iovisor/bpftrace/commit/${{github.sha}} bpftrace-0.14.0/.github/workflows/embedded.yml000066400000000000000000000173651413460502400212460ustar00rootroot00000000000000name: Embedded Builds on: [push, pull_request] jobs: llvm_clang: runs-on: ubuntu-18.04 strategy: matrix: env: - TYPE: Release NAME: vanilla_llvm12+clang+glibc2.27 LLVM_VERSION: 12 STATIC_LINKING: ON STATIC_LIBC: OFF EMBED_BUILD_LLVM: OFF EMBED_USE_LLVM: ON RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: builtin.cgroup,probe.kprobe_offset_fail_size BASE: bionic DISTRO: ubuntu-glibc VENDOR_GTEST: ON - TYPE: Release NAME: vanilla_llvm12+clang+glibc2.23 LLVM_VERSION: 12 STATIC_LINKING: ON STATIC_LIBC: OFF EMBED_BUILD_LLVM: OFF EMBED_USE_LLVM: ON RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: builtin.cgroup,probe.kprobe_offset_fail_size,other.string compare map lookup BASE: xenial DISTRO: ubuntu-glibc CMAKE_EXTRA_FLAGS: "-DCMAKE_CXX_FLAGS='-include /usr/local/include/bcc/compat/linux/bpf.h -D__LINUX_BPF_H__'" VENDOR_GTEST: ON - TYPE: Release NAME: vanilla_llvm+clang+glibc2.27 LLVM_VERSION: 8 STATIC_LINKING: ON STATIC_LIBC: OFF EMBED_BUILD_LLVM: OFF EMBED_USE_LLVM: ON RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: builtin.cgroup,probe.kprobe_offset_fail_size BASE: bionic DISTRO: ubuntu-glibc VENDOR_GTEST: ON - TYPE: Release NAME: vanilla_llvm+clang+glibc2.23 LLVM_VERSION: 8 STATIC_LINKING: ON STATIC_LIBC: OFF EMBED_BUILD_LLVM: OFF EMBED_USE_LLVM: ON RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: builtin.cgroup,probe.kprobe_offset_fail_size,other.string compare map lookup BASE: xenial DISTRO: ubuntu-glibc CMAKE_EXTRA_FLAGS: "-DCMAKE_CXX_FLAGS='-include /usr/local/include/bcc/compat/linux/bpf.h -D__LINUX_BPF_H__'" VENDOR_GTEST: ON - TYPE: Debug NAME: alpine LLVM_VERSION: 9 STATIC_LINKING: ON STATIC_LIBC: ON EMBED_BUILD_LLVM: OFF RUN_ALL_TESTS: 1 TEST_GROUPS_DISABLE: "tools-parsing-test" RUNTIME_TEST_DISABLE: json-output.join_delim,other.string compare map lookup,probe.kprobe_offset_fail_size,probe.uprobe_library,usdt."usdt probes - attach to fully specified probe of child",usdt."usdt probes - all probes by wildcard and file with child",usdt."usdt probes - attach to probe by wildcard and file with child",usdt."usdt probes - attach to probes by wildcard file with child",usdt."usdt probes - attach to probe on multiple files by wildcard",usdt."usdt probes - attach to probe with probe builtin and args by file with child",usdt."usdt probes - list probes by pid in separate mountns",usdt."usdt sized arguments",usdt."usdt - list probes by file with wildcarded probe type",uprobe."uprobes - list probes by pid; uprobes only",uprobe."uprobes - list probes by pid in separate mount namespace",other.positional pointer arithmetics BASE: alpine DISTRO: alpine ALPINE_VERSION: 3.11 VENDOR_GTEST: ON - TYPE: Release NAME: alpine LLVM_VERSION: 9 STATIC_LINKING: ON STATIC_LIBC: ON EMBED_BUILD_LLVM: OFF RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: other.string compare map lookup,probe.kprobe_offset_fail_size,probe.uprobe_library,usdt."usdt probes - attach to fully specified probe of child",usdt."usdt probes - all probes by wildcard and file with child",usdt."usdt probes - attach to probe by wildcard and file with child",usdt."usdt probes - attach to probes by wildcard file with child",usdt."usdt probes - attach to probe on multiple files by wildcard",usdt."usdt probes - attach to probe with probe builtin and args by file with child",usdt."usdt probes - list probes by pid in separate mountns",usdt."usdt sized arguments",usdt."usdt - list probes by file with wildcarded probe type",uprobe."uprobes - list probes by pid; uprobes only",uprobe."uprobes - list probes by pid in separate mount namespace",other.positional pointer arithmetics BASE: alpine DISTRO: alpine ALPINE_VERSION: 3.11 VENDOR_GTEST: ON steps: - uses: actions/checkout@v2 - name: Build docker container run: > docker build -t bpftrace-embedded-${{ matrix.env['BASE'] }} -f docker/Dockerfile.${{ matrix.env['DISTRO'] }} --build-arg bcc_ref=${{ matrix.env['BCC_REF'] }} --build-arg BASE=${{ matrix.env['BASE'] }} --build-arg LLVM_VERSION=${{ matrix.env['LLVM_VERSION'] }} --build-arg ALPINE_VERSION=${{ matrix.env['ALPINE_VERSION'] }} docker/ - name: bpftrace embedded build env: ${{ matrix.env }} run: > docker run --privileged -v $(pwd):$(pwd) -w $(pwd) -v /sys/kernel/debug:/sys/kernel/debug:rw -v /lib/modules:/lib/modules:ro -v /usr/src:/usr/src:ro -e LLVM_VERSION=${LLVM_VERSION} -e STATIC_LINKING=${STATIC_LINKING} -e STATIC_LIBC=${STATIC_LIBC} -e EMBED_USE_LLVM=${EMBED_USE_LLVM} -e EMBED_BUILD_LLVM=${EMBED_BUILD_LLVM} -e RUN_ALL_TESTS=${RUN_ALL_TESTS} -e CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" -e TEST_GROUPS_DISABLE="${TEST_GROUPS_DISABLE}" -e RUNTIME_TEST_DISABLE="${RUNTIME_TEST_DISABLE}" -e VENDOR_GTEST="${VENDOR_GTEST}" bpftrace-embedded-${{ matrix.env['BASE'] }} $(pwd)/build-embedded ${TYPE} -j`nproc` - name: Check linked libs env: ${{ matrix.env }} run: > docker run --privileged -v $(pwd):$(pwd) -w $(pwd) --entrypoint /bin/bash bpftrace-embedded-${{ matrix.env['BASE'] }} -c "[[ -f $(pwd)/build-embedded/src/bpftrace ]] && ! readelf --dynamic $(pwd)/build-embedded/src/bpftrace | grep NEEDED | grep -v 'libm\|libc\|ld-linux\|libpthread\|libdl\|librt\.so'" - name: Strip artifacts env: ${{ matrix.env }} if: matrix.env['TYPE'] == 'Release' run: > docker run --privileged -v $(pwd):$(pwd) -w $(pwd) --entrypoint /bin/bash bpftrace-embedded-${{ matrix.env['BASE'] }} -c "strip --keep-symbol BEGIN_trigger --keep-symbol END_trigger $(pwd)/build-embedded/src/bpftrace" - name: Report size env: ${{ matrix.env }} run: > docker run --privileged -v $(pwd):$(pwd) -w $(pwd) --entrypoint /bin/bash bpftrace-embedded-${{ matrix.env['BASE'] }} -c "echo SIZE: ; du -sh $(pwd)/build-embedded/src/bpftrace" - uses: actions/upload-artifact@v1 with: name: bpftrace-${{ matrix.env['TYPE'] }}-${{ matrix.env['NAME'] }} path: build-embedded/src/bpftrace - uses: actions/upload-artifact@v1 with: name: bpftrace_test-${{ matrix.env['TYPE'] }}-${{ matrix.env['NAME'] }} path: build-embedded/tests/bpftrace_test - name: Authenticate with docker registry if: > matrix.env['TYPE'] == 'Release' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.repository == 'iovisor/bpftrace' env: QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} run: ./docker/scripts/auth.sh ${{ github.repository }} - name: Package docker image and push to quay.io if: > matrix.env['TYPE'] == 'Release' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.repository == 'iovisor/bpftrace' run: > ./docker/scripts/push.sh ${{ github.repository }} ${{ github.ref }} ${{ github.sha }} ${{ matrix.env['NAME'] }} ${{ matrix.env['EDGE'] }} bpftrace-0.14.0/.github/workflows/embedded_llvm.yml000066400000000000000000000027071413460502400222720ustar00rootroot00000000000000name: Embedded LLVM on: push: paths: - docker/Dockerfile.llvm - docker/embedded/* pull_request: paths: - docker/Dockerfile.llvm - docker/embedded/* schedule: - cron: '0 0 1 * *' jobs: publish_llvm_build: runs-on: ubuntu-latest strategy: matrix: llvm_version: [8,12] base: [xenial, bionic] steps: - name: Checkout repo uses: actions/checkout@v2 - name: Build container run: > docker build -t quay.io/iovisor/bpftrace-llvm:${{ matrix.base }}_${{ matrix.llvm_version }} -t quay.io/iovisor/bpftrace-llvm:${{ matrix.base }}_${{ matrix.llvm_version }}_$(date +%s) -f docker/Dockerfile.llvm --build-arg BASE=${{ matrix.base }} --build-arg BULD_TYPE="Release" --build-arg LLVM_VERSION=${{ matrix.llvm_version }} . - name: Login quay.io if: > (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.repository == 'iovisor/bpftrace' env: QUAY_USER: "iovisor+bpftrace_buildbot" QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} run: 'echo "${QUAY_TOKEN}" | docker login -u="${QUAY_USER}" --password-stdin quay.io' - name: Publish container if: > (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.repository == 'iovisor/bpftrace' run: docker push --all-tags quay.io/iovisor/bpftrace-llvm bpftrace-0.14.0/.gitignore000066400000000000000000000003011413460502400153430ustar00rootroot00000000000000build/ build-*/ tests/runtime/*.pyc tests/data/dwarf_data.h .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 ignored/ bpftrace-0.14.0/.lgtm.yml000066400000000000000000000006121413460502400151240ustar00rootroot00000000000000extraction: cpp: prepare: packages: - bison - cmake - flex - g++ - git - libelf-dev - libgtest-dev - libgmock-dev - zlib1g-dev - libfl-dev - systemtap-sdt-dev - binutils-dev - llvm-7-dev - llvm-7-runtime - libclang-7-dev - clang-7 - libbpfcc-dev bpftrace-0.14.0/CHANGELOG.md000066400000000000000000002627651413460502400152140ustar00rootroot00000000000000# 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.14.0] 2021-10-22 #### Added - Build time dependency on cereal - [#1893](https://github.com/iovisor/bpftrace/pull/1893) - Build time dependency on asciidoctor for man page generation - [#1927] (https://github.com/iovisor/bpftrace/pull/1927) - Support microsecond timestamps in stftime() - [#1922](https://github.com/iovisor/bpftrace/pull/1922) - Add `_` as integer literal digit separator - [#1900](https://github.com/iovisor/bpftrace/pull/1900) - Support for C style integer suffix in parser - [#1938](https://github.com/iovisor/bpftrace/pull/1938) - Add C like pointer arithmetic - [#1881](https://github.com/iovisor/bpftrace/pull/1881) - Automatic resolution of library paths for uprobes - [#1971](https://github.com/iovisor/bpftrace/pull/1971) - Support positional parameters as integer literals - [#1982](https://github.com/iovisor/bpftrace/pull/1982) - Access to uprobe arguments by name - [#1994](https://github.com/iovisor/bpftrace/pull/1994) #### Changed - Prevent LLVM from unrolling loops - [#1967](https://github.com/iovisor/bpftrace/pull/1967) #### Deprecated #### Removed #### Fixed - Fix memory leaks in struct types - [#1885](https://github.com/iovisor/bpftrace/pull/1885) - Fix strncmp() when N is bigger than on-stack buffer - [#1974](https://github.com/iovisor/bpftrace/pull/1974) - Fix strncmp() to check for NUL terminator - [#1974](https://github.com/iovisor/bpftrace/pull/1974) - Fix unroll() with async calls - [#1972](https://github.com/iovisor/bpftrace/pull/1972) - Fix string comparison codegen - [#1979](https://github.com/iovisor/bpftrace/pull/1979) - Fix verifier error when accessing same tracepoint field twice - [#2008](https://github.com/iovisor/bpftrace/pull/2008) - Fix reading too many bits for <64 bit kfunc args - [#2014](https://github.com/iovisor/bpftrace/pull/2014) - Fix misaligned stack access for map keys - [#2012](https://github.com/iovisor/bpftrace/pull/2012) #### Tools #### Documentation - Write new man page for `bpftrace(8)` - [#1711](https://github.com/iovisor/bpftrace/pull/1711) ## [0.13.0] 2021-07-01 #### Added - Warn if attaching a kprobe to a non-traceable function - [#1835](https://github.com/iovisor/bpftrace/pull/1835) - Support for `-k[k]` and `elapsed` in `iter` probes - [#1882](https://github.com/iovisor/bpftrace/pull/1882) #### Changed - Disallow accessing common tracepoint fields - [#1810](https://github.com/iovisor/bpftrace/pull/1810) - Improve JSON printing (nested structs) - [#1778](https://github.com/iovisor/bpftrace/pull/1778) - Return 1 from tracepoint probes - [#1857](https://github.com/iovisor/bpftrace/pull/1857) - Preserve original order of struct types - [#1850](https://github.com/iovisor/bpftrace/pull/1850) - Forbid casting from/to struct types - [#1873](https://github.com/iovisor/bpftrace/pull/1873) #### Deprecated #### Removed #### Fixed - Fix single arg wildcard probe listing - [#1775](https://github.com/iovisor/bpftrace/pull/1775) - Fix --info reporting wrong libbpf build info - [#1776](https://github.com/iovisor/bpftrace/pull/1776) - Reduce frequency of lost stack traces - [#1812](https://github.com/iovisor/bpftrace/pull/1812) - Make kaddr() report failure for unknown kernel symbols - [#1836](https://github.com/iovisor/bpftrace/pull/1836) - Fix false non-traceable function warnings - [#1866](https://github.com/iovisor/bpftrace/pull/1866) - Fix memory leak in clang parser - [#1878](https://github.com/iovisor/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/iovisor/bpftrace/pull/1492) - Allow wildcards for tracepoint categories - [#1445](https://github.com/iovisor/bpftrace/pull/1445) - Add wildcard support for kfunc probe types - [#1410](https://github.com/iovisor/bpftrace/pull/1410) - Add builtin function: `strftime` - [#1387](https://github.com/iovisor/bpftrace/pull/1387) - Fix `printf` not allowing format specifiers to be directly followed by alphabetic characters - [#1414](https://github.com/iovisor/bpftrace/pull/1414) - Fix `top` and `div` arguments of `print()` not working for Type::avg maps - [#1416](https://github.com/iovisor/bpftrace/pull/1416) - Add an option to disable warning messages - [#1444](https://github.com/iovisor/bpftrace/pull/1444) - Support scientific notation for integer literals - [#1476](https://github.com/iovisor/bpftrace/pull/1476) - List retprobes - [#1484](https://github.com/iovisor/bpftrace/pull/1484) - Resolve unknown typedefs using BTF and give a hint when a type cannot be found - [#1485](https://github.com/iovisor/bpftrace/pull/1485) - Support multi-matched globbed targets for uprobe and ustd probes - [#1499](https://github.com/iovisor/bpftrace/pull/1499) - Positional parameters: support numbers as strings and params as string literals - [#1514](https://github.com/iovisor/bpftrace/pull/1514) - Support for tracepoint __data_loc fields - [#1542](https://github.com/iovisor/bpftrace/pull/1542) - Set addrspace info for various builtins - [#1504](https://github.com/iovisor/bpftrace/pull/1504) - Support watchpoint for kernel space address - [#1552](https://github.com/iovisor/bpftrace/pull/1552) - Support for pointer to pointer - [#1557](https://github.com/iovisor/bpftrace/pull/1557) - Support for uprobe refcounts - [#1567](https://github.com/iovisor/bpftrace/pull/1567) - Add basic options and documentations for fuzzing - [#1601](https://github.com/iovisor/bpftrace/pull/1601) - Disable `str($# + 1)` - [#1619](https://github.com/iovisor/bpftrace/issues/1619) - Array improvements (support assignment to variables and usage as a map key) - [#1656](https://github.com/iovisor/bpftrace/pull/1656) - Add builtin function: `macaddr` - [#1647](https://github.com/iovisor/bpftrace/pull/1647) - Add support for usdt arguments utilising the index register and scale - [#1684](https://github.com/iovisor/bpftrace/pull/1684) - Add basic mips64 support - [#1599](https://github.com/iovisor/bpftrace/pull/1599) - Printing structures - [#1705](https://github.com/iovisor/bpftrace/pull/1705) - Array indexing on pointers - [#1739](https://github.com/iovisor/bpftrace/pull/1739) #### Changed - Warn if using `print` on `stats` maps with top and div arguments - [#1433](https://github.com/iovisor/bpftrace/pull/1433) - Prefer BTF data if available to resolve tracepoint arguments - [#1439](https://github.com/iovisor/bpftrace/pull/1439) - Improve error messages for kfunc probe types - [#1451](https://github.com/iovisor/bpftrace/pull/1451) - Better handling of empty usdt namespaces - [#1486](https://github.com/iovisor/bpftrace/pull/1486) - Switch `nsecs` to `ktime_get_boot_ns` - [#1475](https://github.com/iovisor/bpftrace/pull/1475) - Tracepoint __data_loc fields are renamed from `args->data_loc_name` to `args->name` - [#1542](https://github.com/iovisor/bpftrace/pull/1542) - Change a part of the message of '-v' output - [#1553](https://github.com/iovisor/bpftrace/pull/1553) - Improve tuple assignment error message - [#1563](https://github.com/iovisor/bpftrace/pull/1563) - Remove "BTF: using data from ..." message when using -v flag - [#1554](https://github.com/iovisor/bpftrace/pull/1554) - Add -q option for quiet - [#1616](https://github.com/iovisor/bpftrace/pull/1616) - Optimize unknown/incomplete types resolution - [#1571](https://github.com/iovisor/bpftrace/pull/1571) - Do not check size of the format string of `printf` - [#1538](https://github.com/iovisor/bpftrace/pull/1538) - Unify semantics of wildcards in probe listing and attachement - [#1549](https://github.com/iovisor/bpftrace/pull/1549) - Improve codegen for structs and arrays - [#1705](https://github.com/iovisor/bpftrace/pull/1705) - Do not unpack in-kernel headers if system has BTF - [#1740](https://github.com/iovisor/bpftrace/pull/1740) #### Deprecated #### Removed - Disable some kfunc probes whose tracing crashes - [#1432](https://github.com/iovisor/bpftrace/pull/1432) #### Fixed - Fix negative overflow bug and unstable tests in PR #1416 - [#1436](https://github.com/iovisor/bpftrace/pull/1436) - Fix `print` outputs nothing when used on hist() maps with large top args - [#1437](https://github.com/iovisor/bpftrace/pull/1437) - Fix array indexing regression - [#1457](https://github.com/iovisor/bpftrace/pull/1457) - Fix type resolution for struct field access via variables - [#1450](https://github.com/iovisor/bpftrace/pull/1450) - Fix wrong setting of vmlinux_location.raw when offset kprobe used - [#1530](https://github.com/iovisor/bpftrace/pull/1530) - Fix pointer arithmetic for positional parameters - [#1514](https://github.com/iovisor/bpftrace/pull/1514) - SEGV when using perf format for stacks - [#1524](https://github.com/iovisor/bpftrace/pull/1524) - Fix llvm errors of PositonalParameter - [#1565](https://github.com/iovisor/bpftrace/pull/1565) - Error if Positional Params num is zero - [#1568](https://github.com/iovisor/bpftrace/issues/1568) - Fix LNOT - [#1570](https://github.com/iovisor/bpftrace/pull/1570) - Fix invalid cast handling in tuple - [#1572](https://github.com/iovisor/bpftrace/pull/1572) - Check string comparison size - [#1573](https://github.com/iovisor/bpftrace/pull/1573) - Fix a possible integer overflow - [#1580](https://github.com/iovisor/bpftrace/pull/1580) - Printing of small integers with `printf` - [#1532](https://github.com/iovisor/bpftrace/pull/1532) - Fix bitfield access for big endian - [#1628](https://github.com/iovisor/bpftrace/pull/1628) - Error if using negative length in str() and buf() - [#1621](https://github.com/iovisor/bpftrace/pull/1621) - Only create int type Identifier when it is used in sizeof() - [#1622](https://github.com/iovisor/bpftrace/pull/1622) - Check exponent value can be expressed in uint64_t - [#1623](https://github.com/iovisor/bpftrace/pull/1623) - Fix tracing of usdt probes across namespaces - [#1637](https://github.com/iovisor/bpftrace/pull/1637) - Disable reg() for kfunc - [#1646](https://github.com/iovisor/bpftrace/pull/1646) - Fix several undefined behavior - [#1645](https://github.com/iovisor/bpftrace/pull/1645) - Fix invalid size crash when using strftime() inside a tuple - [#1658](https://github.com/iovisor/bpftrace/pull/1658) - Don't create a tuple if an element size if zero - [#1653](https://github.com/iovisor/bpftrace/pull/1653) - Support clear() and delete() on a count()-based map without a key - [#1639](https://github.com/iovisor/bpftrace/pull/1639) - Add workaround for too deep or long macros - [#1650](https://github.com/iovisor/bpftrace/pull/1650) - Fix attaching to usdt probes in shared libraries - [#1600](https://github.com/iovisor/bpftrace/pull/1600) - Fix attaching to multiple usdt probe locations with the same label - [#1681](https://github.com/iovisor/bpftrace/pull/1681) - Fix signed extension of usdt arguments to the internal 64-bit integer type - [#1684](https://github.com/iovisor/bpftrace/pull/1684) #### Tools - Hook up execsnoop.bt script onto `execveat` call - [#1490](https://github.com/iovisor/bpftrace/pull/1490) - Support new capabilities for capable.bt - [#1498](https://github.com/iovisor/bpftrace/pull/1498) - Add disk field to biosnoop - [#1660](https://github.com/iovisor/bpftrace/pull/1660) #### Documentation - Document uptr() and kptr() function - [#1626](https://github.com/iovisor/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/iovisor/bpftrace/pull/1589) ## [0.11.2] 2020-10-30 LLVM 11 support release ### Added Add LLVM11 build support - [#1578](https://github.com/iovisor/bpftrace/pull/1578) ## [0.11.1] 2020-09-22 Bug fix release for the [Docker build](https://quay.io/repository/iovisor/bpftrace) ### Fixed - Don't strip END_trigger - [#1513](https://github.com/iovisor/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/iovisor/bpftrace/pull/1388) - Support for s390x - [#1241](https://github.com/iovisor/bpftrace/pull/1241) - `buf` a new function that makes it possible to safely print arbitrary binary data - [#1107](https://github.com/iovisor/bpftrace/pull/1107) - A new function, `sizeof`, which returns the size of an expression, similar to `sizeof` in C - [#1269](https://github.com/iovisor/bpftrace/pull/1269) - C style while loop support, `while ($a < 100) { $a++ }` - [#1066](https://github.com/iovisor/bpftrace/pull/1066) - Using a BTF enum value will pull in the entire enum definition - [#1274](https://github.com/iovisor/bpftrace/pull/1274) - Add support of using positional params in unroll and increase the unroll limit to 100 - [#1286](https://github.com/iovisor/bpftrace/pull/1286) - Support for piping scripts in via stdin - [#1310](https://github.com/iovisor/bpftrace/pull/1310) - Don't require if --btf is specified - [#1315](https://github.com/iovisor/bpftrace/pull/1315) - Silence errors about `modprobe` not being found - [#1314](https://github.com/iovisor/bpftrace/pull/1314) - With --btf, do not use for resolving tracepoint defs - [#1318](https://github.com/iovisor/bpftrace/pull/1318) - Add environment variable, BPFTRACE_PERF_RB_PAGES, to tune perf ring buffer size - [#1329](https://github.com/iovisor/bpftrace/pull/1329) - Add --usdt-file-activation to activate usdt semaphores by file name - [#1317](https://github.com/iovisor/bpftrace/pull/1317) - Introduce `-k` and `-kk` options. Emit a warning when a bpf helper returns an error - [#1276](https://github.com/iovisor/bpftrace/pull/1276) - Add tuples to language - [#1326](https://github.com/iovisor/bpftrace/pull/1326) - Add support for listing struct/union/enum definitions using BTF - [#1340](https://github.com/iovisor/bpftrace/pull/1340) - Add libbpf build into in --info - [#1367](https://github.com/iovisor/bpftrace/pull/1367) - Add support for time units `us` and `hz` for probe `interval` - [#1377](https://github.com/iovisor/bpftrace/pull/1377) - Add support for non-map print() - [#1381](https://github.com/iovisor/bpftrace/pull/1381) - Enable `printf`, `cat` and `system` to have more than 7 arguments - [#1404](https://github.com/iovisor/bpftrace/pull/1404) - Enable the `ternary` operator to evaluate builtin calls - [#1405](https://github.com/iovisor/bpftrace/pull/1405) #### Changed - Require C++17 and CMake 3.8 for building bpftrace - [#1200](https://github.com/iovisor/bpftrace/pull/1200) - [#1259](https://github.com/iovisor/bpftrace/pull/1259) - Allow positional parameters in probe attachpoint definitions - [#1328](https://github.com/iovisor/bpftrace/pull/1328) - Only list uprobe and usdt probes when `-p` is given - [#1340](https://github.com/iovisor/bpftrace/pull/1340) - Remove address space memory limit - [#1358](https://github.com/iovisor/bpftrace/pull/1358) #### Deprecated #### Removed - Drop LLVM 5 support - [#1215](https://github.com/iovisor/bpftrace/issues/1215) - Remove the --btf option - [#1669](https://github.com/iovisor/bpftrace/pull/1669) #### Fixed - Various big endian related fixes - [#1241](https://github.com/iovisor/bpftrace/pull/1241) - Type check the `cond` of if and ternary statements - [#1229](https://github.com/iovisor/bpftrace/pull/1229) - Fix usdt reads in various architecture - [#1325](https://github.com/iovisor/bpftrace/pull/1325) - Attach to duplicated USDT markers - [#1341](https://github.com/iovisor/bpftrace/pull/1341) - Fix `KBUILD_MODNAME` - [#1352](https://github.com/iovisor/bpftrace/pull/1352) - Fix `ntop()` not accepting tracepoint arguments - [#1365](https://github.com/iovisor/bpftrace/pull/1365) - Fix attaching to usdt probes in multiple binaries - [#1356](https://github.com/iovisor/bpftrace/pull/1356) - Decrement usdt semaphore count after bpftrace execution - [#1370](https://github.com/iovisor/bpftrace/pull/1370) - Reduce high memory consumption when using usdt semaphore - [#1374](https://github.com/iovisor/bpftrace/pull/1374) - Remove registers that are not in struct pt_regs (x86-64) - [#1383](https://github.com/iovisor/bpftrace/issues/1383) - Ignore trailing kernel module annotation for k[ret]probe's - [#1413](https://github.com/iovisor/bpftrace/pull/1413) #### Tools #### Documentation - Clean up README - [#1273](https://github.com/iovisor/bpftrace/pull/1273) - Add missing `struct` keyword to examples in the one liner tutorial - [#1275](https://github.com/iovisor/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/iovisor/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.14.0/CMakeLists-LLVM.txt000066400000000000000000000015731413460502400166770ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) project(bpftrace-llvm-builder) include(GNUInstallDirs) set(LLVM_VERSION "8" CACHE STRING "LLVM version to build") set(STATIC_LINKING ON) set(STATIC_LIBC OFF) set(EMBED_USE_LLVM ON) set(EMBED_BUILD_LLVM ON) set(EMBED_LLVM_VERSION ${LLVM_VERSION} CACHE STRING "LLVM version to build") message(STATUS "Using LLVM: ${EMBED_LLVM_VERSION}") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed) include(embed_helpers) set (CMAKE_CXX_STANDARD 17) set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") set(CMAKE_LINK_SEARCH_START_STATIC TRUE) set(CMAKE_LINK_SEARCH_END_STATIC TRUE) set(FIND_LIBRARY_USE_LIB64_PATHS TRUE) cmake_policy(SET CMP0075 NEW) include(embed_llvm) include(embed_clang) bpftrace-0.14.0/CMakeLists.txt000066400000000000000000000232601413460502400161240ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13.0) project(bpftrace) # bpftrace version number components. set(bpftrace_VERSION_MAJOR 0) set(bpftrace_VERSION_MINOR 14) set(bpftrace_VERSION_PATCH 0) include(GNUInstallDirs) set(WARNINGS_AS_ERRORS OFF CACHE BOOL "Build with -Werror") set(STATIC_LINKING OFF CACHE BOOL "Build bpftrace as a statically linked executable") set(STATIC_LIBC OFF CACHE BOOL "Attempt to embed libc, only known to work with musl. Has issues with dlopen.") set(EMBED_USE_LLVM OFF CACHE BOOL "Use a prebuilt embedded LLVM, speeds up the build process") set(EMBED_BUILD_LLVM OFF CACHE BOOL "Build Clang&LLVM static libs as an ExternalProject and link to these instead of system libs.") set(EMBED_LLVM_VERSION "8" CACHE STRING "Embedded LLVM/Clang version to build and link against.") set(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(VENDOR_GTEST OFF CACHE BOOL "Clone gtest from github") set(BUILD_FUZZ OFF CACHE BOOL "Build bpftrace for fuzzing") set(USE_LIBFUZZER OFF CACHE BOOL "Use libfuzzer for fuzzing") set(FUZZ_TARGET "codegen" CACHE STRING "Fuzzing target") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) if(EMBED_BUILD_LLVM) set(EMBED_USE_LLVM ON) endif() if(EMBED_USE_LLVM AND NOT EMBED_BUILD_LLVM) set(EMBED_LLVM_PATH "/usr/local/lib") endif() if(EMBED_USE_LLVM OR STATIC_LIBC) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed) include(embed_helpers) if (NOT STATIC_LINKING) set(CONFIG_ERROR "Dependencies can only be embedded for a static build.\n" "Enable STATIC_LINKING=ON to embed static libs.") message(FATAL_ERROR ${CONFIG_ERROR}) elseif(STATIC_LIBC) message(WARNING "static libc is known to cause problems, consider STATIC_LIBC=OFF. Proceed at your own risk") #iovisor/bpftrace/issues/266 endif() endif() set (CMAKE_CXX_STANDARD 17) set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_EXTENSIONS OFF) add_compile_options("-Wall") add_compile_options("-Wextra") add_compile_options("-Wundef") add_compile_options("-Wpointer-arith") add_compile_options("-Wcast-align") add_compile_options("-Wwrite-strings") add_compile_options("-Wcast-qual") #add_compile_options("-Wconversion") add_compile_options("-Wunreachable-code") #add_compile_options("-Wformat=2") add_compile_options("-Wdisabled-optimization") if (WARNINGS_AS_ERRORS) add_compile_options("-Werror") endif() # 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(LibBcc REQUIRED) include_directories(SYSTEM ${LIBBCC_INCLUDE_DIRS}) find_package(LibElf REQUIRED) include_directories(SYSTEM ${LIBELF_INCLUDE_DIRS}) find_package(LibCereal REQUIRED) include_directories(SYSTEM ${CEREAL_INCLUDE_DIRS}) find_package(BISON REQUIRED) find_package(FLEX REQUIRED) bison_target(bison_parser src/parser.yy ${CMAKE_BINARY_DIR}/parser.tab.cc 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 ${BISON_bison_parser_OUTPUTS} ${FLEX_flex_lexer_OUTPUTS}) target_compile_options(parser PRIVATE "-w") target_include_directories(parser PUBLIC src src/ast ${CMAKE_BINARY_DIR}) 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(LibBpf) find_package(LibBfd) find_package(LibOpcodes) find_package(LibDw) if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() if(STATIC_LINKING) set(CMAKE_REQUIRED_LIBRARIES bcc bcc_bpf bpf elf z) else() set(CMAKE_REQUIRED_LIBRARIES ${LIBBCC_LIBRARIES}) if (LIBBPF_FOUND) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LIBBPF_LIBRARIES}) endif() endif(STATIC_LINKING) get_filename_component(LIBBCC_LIBDIR ${LIBBCC_LIBRARIES} DIRECTORY) set(CMAKE_REQUIRED_LINK_OPTIONS -L${LIBBCC_LIBDIR}) check_symbol_exists(bcc_prog_load "${LIBBCC_INCLUDE_DIRS}/bcc/libbpf.h" HAVE_BCC_PROG_LOAD) check_symbol_exists(bcc_create_map "${LIBBCC_INCLUDE_DIRS}/bcc/libbpf.h" HAVE_BCC_CREATE_MAP) check_symbol_exists(bcc_elf_foreach_sym "${LIBBCC_INCLUDE_DIRS}/bcc/bcc_elf.h" HAVE_BCC_ELF_FOREACH_SYM) check_symbol_exists(bpf_attach_kfunc "${LIBBCC_INCLUDE_DIRS}/bcc/libbpf.h" HAVE_BCC_KFUNC) check_symbol_exists(bcc_usdt_addsem_probe "${LIBBCC_INCLUDE_DIRS}/bcc/bcc_usdt.h" HAVE_BCC_USDT_ADDSEM) check_symbol_exists(bcc_procutils_which_so "${LIBBCC_INCLUDE_DIRS}/bcc/bcc_proc.h" HAVE_BCC_WHICH_SO) # bcc_prog_load_xattr needs struct bpf_load_program_attr, # which is defined in libbpf if (LIBBPF_FOUND) check_symbol_exists(bcc_prog_load_xattr "${LIBBCC_INCLUDE_DIRS}/bcc/libbpf.h" HAVE_BCC_PROG_LOAD_XATTR) endif() set(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LINK_OPTIONS) if(${LIBBFD_FOUND} AND ${LIBOPCODES_FOUND}) set(HAVE_BFD_DISASM TRUE) endif() include(CheckIncludeFile) check_include_file("sys/sdt.h" HAVE_SYSTEMTAP_SYS_SDT_H) if (EMBED_USE_LLVM) include(embed_llvm) else() # 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 6) set(MAX_LLVM_MAJOR 14) 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}) endif() add_definitions(-DLLVM_VERSION_MAJOR=${LLVM_VERSION_MAJOR}) add_definitions(-DLLVM_VERSION_MINOR=${LLVM_VERSION_MINOR}) add_definitions(-DLLVM_VERSION_PATCH=${LLVM_VERSION_PATCH}) if(${LLVM_VERSION_MAJOR} VERSION_GREATER_EQUAL 11) set(LLVM_ORC_V2) add_definitions(-DLLVM_ORC_V2) message(STATUS "Using LLVM orcv2") else() add_definitions(-DLLVM_ORC_V1) endif() if(EMBED_USE_LLVM) include(embed_clang) else() find_package(Clang REQUIRED) include_directories(SYSTEM ${CLANG_INCLUDE_DIRS}) endif() # BPFtrace compile definitions set(BPFTRACE_FLAGS) if (ALLOW_UNSAFE_PROBE) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_UNSAFE_PROBE) endif(ALLOW_UNSAFE_PROBE) 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_BCC_PROG_LOAD) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BCC_PROG_LOAD) endif(HAVE_BCC_PROG_LOAD) if(HAVE_BCC_CREATE_MAP) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BCC_CREATE_MAP) endif(HAVE_BCC_CREATE_MAP) if(HAVE_BCC_ELF_FOREACH_SYM) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BCC_ELF_FOREACH_SYM) endif(HAVE_BCC_ELF_FOREACH_SYM) if(HAVE_BCC_USDT_ADDSEM) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BCC_USDT_ADDSEM) endif(HAVE_BCC_USDT_ADDSEM) if(HAVE_BCC_WHICH_SO) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BCC_WHICH_SO) endif(HAVE_BCC_WHICH_SO) if(LIBBCC_ATTACH_KPROBE_SIX_ARGS_SIGNATURE) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" LIBBCC_ATTACH_KPROBE_SIX_ARGS_SIGNATURE) endif(LIBBCC_ATTACH_KPROBE_SIX_ARGS_SIGNATURE) if(LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE) endif(LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE) if (HAVE_BCC_KFUNC) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BCC_KFUNC) endif(HAVE_BCC_KFUNC) if (HAVE_LIBBPF_LINK_CREATE OR HAVE_BCC_PROG_LOAD_XATTR) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_BPF_H) endif() if (LIBBPF_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF) endif(LIBBPF_FOUND) if (HAVE_LIBBPF_MAP_BATCH) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_MAP_BATCH) endif() if (HAVE_LIBBPF_LINK_CREATE) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_LINK_CREATE) endif() if (HAVE_BCC_PROG_LOAD_XATTR) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BCC_PROG_LOAD_XATTR) endif() 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) endif(HAVE_BFD_DISASM) if (LIBBPF_BTF_DUMP_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_BTF_DUMP) if (HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL) endif() endif(LIBBPF_BTF_DUMP_FOUND) if (LIBDW_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBDW) endif () add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(tests) endif() add_subdirectory(resources) add_subdirectory(tools) if (ENABLE_MAN) add_subdirectory(man) endif(ENABLE_MAN) bpftrace-0.14.0/CONTRIBUTING-TOOLS.md000066400000000000000000000134061413460502400165140ustar00rootroot00000000000000# Contributing bpftrace/eBPF tools If you want to contribute tools to bpftrace, please read this checklist first. _(Written by Brendan Gregg. Adapted from the [bcc version](https://github.com/iovisor/bcc/blob/master/CONTRIBUTING-SCRIPTS.md))._ bpftrace tool development checklist: 1. **Research the topic landscape**. Learn the existing tools and metrics (incl. from /proc). Determine what real world problems exist and need solving. We have too many tools and metrics as it is, we don't need more "I guess that's useful" tools, we need more "ah-hah! I couldn't do this before!" tools. Consider asking other developers about your idea. Many of us can be found in IRC, in the #iovisor channel on irc.oftc.net. There's also the iovisor mailing list (see the README.md), and github for issues. 1. **Create a known workload for testing**. This might involving writing a 10 line C program, using a micro-benchmark, or just improvising at the shell. If you don't know how to create a workload, learn! Figuring this out will provide invaluable context and details that you may have otherwise overlooked. Sometimes it's easy, and I'm able to just use dd(1) from /dev/urandom or a disk device to /dev/null. It lets me set the I/O size, count, and provides throughput statistics for cross-checking my tool output. But other times I need a micro-benchmark, or some C. 1. **Write the tool to solve the problem and no more**. Unix philosophy: do one thing and do it well. netstat doesn't have an option to dump packets, tcpdump-style. They are two different tools. 1. **Consider bcc for custom output and options**. Need to really customize your output? Want to support a variety of command line options? It sounds like your tool may be better as a bcc tool, which currently supports these using Python (and other) interfaces [bcc](https://github.com/iovisor/bcc). 1. **Check your tool correctly measures your known workload**. If possible, run a prime number of events (eg, 23) and check that the numbers match. Try other workload variations. 1. **Use other observability tools to perform a cross-check or sanity check**. Eg, imagine you write a PCI bus tool that shows current throughput is 28 Gbytes/sec. How could you sanity test that? Well, what PCI devices are there? Disks and network cards? Measure their throughput (iostat, nicstat, sar), and check if is in the ballpark of 28 Gbytes/sec (which would include PCI frame overheads). Ideally, your numbers match. 1. **Measure the overhead of the tool**. If you are running a micro-benchmark, how much slower is it with the tool running. Is more CPU consumed? Try to determine the worst case: run the micro-benchmark so that CPU headroom is exhausted, and then run the bpftrace tool. Can overhead be lowered? 1. **Test again, and stress test**. You want to discover and fix all the bad things before others hit them. 1. **Consider your own repository**. Your tool does not need to be here! bpftrace makes it very easy to create new tools, perhaps too easy. As the previous items described, it's possible to create tools that print metrics that are incorrect, or cause too high overhead. Tools here will likely be run on production servers as root, at many companies, and we want to err on the side of caution. You can always create your own repository of bpftrace tools, and once they have had some exposure, testing, and bug fixes, consider contributing them here. 1. **Concise, intuitive, self-explanatory output**. The default output should meet the common need concisely. Consider including a startup message that's self-explanatory, eg "Tracing block I/O. Output every 1 seconds. Ctrl-C to end.". Also, try hard to keep the output less than 80 characters wide, especially the default output of the tool. That way, the output not only fits on the smallest reasonable terminal, it also fits well in slide decks, blog posts, articles, and printed material, all of which help education and adoption. Publishers of technical books often have templates they require books to conform to: it may not be an option to shrink or narrow the font to fit your output. 1. **Check style**: Do you have a consistent convention for indentation, variable names, and comment style? You can follow the lead from the other tools. 1. **Write an _example.txt file**. Copy the style in tools/biolatency_example.txt: start with an intro sentence, then have examples, and finish with the USAGE message. Explain everything: the first example should explain what we are seeing, even if this seems obvious. For some people it won't be obvious. Also explain why we are running the tool: what problems it's solving. It can take a long time (hours) to come up with good examples, but it's worth it. These will get copied around (eg, presentations, articles). 1. **Read your example.txt file**. Does this sound too niche or convoluted? Are you spending too much time explaining caveats? These can be hints that perhaps you should fix your tool, or abandon it! I've abandoned many tools at this stage. 1. **Write a man page**. Either ROFF (.8), markdown (.md), or plain text (.txt): so long as it documents the important sections, particularly columns (fields) and caveats. These go under man/man8. See the other examples. Include a section on overhead, and pull no punches. It's better for end users to know about high overhead beforehand, than to discover it the hard way. Also explain caveats. Don't assume those will be obvious to tool users. 1. **Read your man page**. For ROFF: nroff -man filename. Like before, this exercise is like saying something out loud. Does it sound too niche or convoluted? Again, hints that you might need to go back and fix things, or abandon it. 1. **Spell check your documentation**. Use a spell checker like aspell to check your document quality before committing. 1. **Add an entry to README.md**. 1. If you made it this far, pull request! bpftrace-0.14.0/INSTALL.md000066400000000000000000000356741413460502400150300ustar00rootroot00000000000000# bpftrace Install - [Linux Kernel Requirements](#linux-kernel-requirements) - [Package install](#package-install) - [Ubuntu](#ubuntu-packages) - [Fedora](#fedora-package) - [Gentoo](#gentoo-package) - [Debian](#debian-package) - [openSUSE](#openSUSE-package) - [CentOS](#CentOS-package) - [Docker images](#docker-images) - [Copying bpftrace binary docker](#copying-bpftrace-binary-from-docker) - [Kernel headers install](#kernel-headers-install) - [Building bpftrace](#building-bpftrace) - [Ubuntu](#ubuntu) - [Fedora](#fedora) - [Amazon Linux](#amazon-linux) - (*please add sections for other OSes)* - [Using Docker](#using-docker) - [Generic build](#generic-build-process) - [Disable Lockdown](#disable-lockdown) # Linux Kernel Requirements It is recommended that you are running a Linux 4.9 kernel or higher. Some tools may work on older kernels, but these old kernels are no longer tested. To explain this requirement, these are the kernel versions where major features were added: - 4.1 - kprobes - 4.3 - uprobes - 4.6 - stack traces, count and hist builtins (use PERCPU maps for accuracy and efficiency) - 4.7 - tracepoints - 4.9 - timers/profiling Minor improvements have been added in later kernels, so newer than 4.9 is preferred. Your kernel also 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. # Package install ## Ubuntu packages ``` sudo apt-get install -y bpftrace ``` Should work on Ubuntu 19.04 and later. On Ubuntu 16.04 and later, bpftrace is also available as a snap package (https://snapcraft.io/bpftrace), however, the snap provides extremely limited file permissions so the --devmode option should be specified on installation in order avoid file access issues. ``` sudo snap install --devmode bpftrace sudo snap connect bpftrace:system-trace ``` The snap package also currently has issues with uprobes ([#829](https://github.com/iovisor/bpftrace/issues/829)). ## 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). # Docker images Each push to master will result in a docker image being built and pushed to the quay.io container hosting service. This publishes the docker embedded build linked to glibc, packaged in a minimal ubuntu container. This allows for such an invocation of bpftrace: ``` $ docker run -ti -v /usr/src:/usr/src:ro \ -v /lib/modules/:/lib/modules:ro \ -v /sys/kernel/debug/:/sys/kernel/debug:rw \ --net=host --pid=host --privileged \ quay.io/iovisor/bpftrace:latest \ tcplife.bt Attaching 3 probes... PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS ``` The following tags are published for all builds: - `quay.io/iovisor/bpftrace:${GIT_SHA}-${TYPE_TAG}`- eg `69149e94952db2eea579ad40e15fbc67c7b810d5-vanilla_llvm_clang_glibc2.27` - `quay.io/iovisor/bpftrace:${GIT_REF}-${TYPE_TAG}`- eg `master-vanilla_llvm_clang_glibc2.23` or `v0.9.5-vanilla_llvm_clang_glibc2.23` If the build is on the master branch, it also publishes these additional tags: - `quay.io/iovisor/bpftrace:${GIT_REF}`- eg `master` or `v0.9.5` - `quay.io/iovisor/bpftrace:${GIT_SHA}`- eg `69149e94952db2eea579ad40e15fbc67c7b810d5` - `quay.io/iovisor/bpftrace:latest` If the build type name ends with `_edge`, and `EDGE=ON` is set, and the build is on master, these tags are not pushed, and instead the `edge` tag is pushed: - `quay.io/iovisor/bpftrace:edge` This `:edge` build is likely less stable than `:latest` or tagged revisions, but builds against bcc master and the latest LLVM supported by bpftrace. The principal goal of the `:edge` build is to help detect integration issues early, and make all latest features available, but that may also make it less stable for day-to-day or production use. If using floating tagged images, such as branch tags, `:latest`, or `:edge` or `:master`, it may be necessary to run `docker pull` explicitly, to ensure that the tag is updated. The [full list of tags](https://quay.io/repository/iovisor/bpftrace?tab=tags) can be used to search for tags, and the history of all tags is recorded on [quay.io](https://quay.io/repository/iovisor/bpftrace?tab=history), and the distributed images are regularly scanned by a vulnerability scanner. ## Copying bpftrace binary from docker As docker builds produce a bpftrace binary on every push to master, they also allow for a convenient way to distribute bpftrace binaries. The only software requirement to run bpftrace is a version of glibc that is the same or newer as what it was built at. For this reason, an older glibc - 2.23 is provided for all builds, it can be pulled with: ``` docker pull quay.io/iovisor/bpftrace:master-vanilla_llvm_clang_glibc2.23 ``` To copy the binary out of bpftrace in the current directory: ``` $ docker run -v $(pwd):/output quay.io/iovisor/bpftrace:master-vanilla_llvm_clang_glibc2.23 \ /bin/bash -c "cp /usr/bin/bpftrace /output" $ ./bpftrace -V v0.9.4 ``` bpftrace currently links to glibc 2.27 from Ubuntu Bionic by default, though this should be portably to any glibc-based OS, such as Fedora, Chromium OS, etc. ## 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 ``` # 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 The version of `bcc` available in Ubuntu 19.04 (Disco) is new enough so compilation is not required, install with: ``` sudo apt-get install -y libbpfcc-dev ``` ### Building `bpftrace` ``` sudo apt-get update sudo apt-get install -y \ bison \ cmake \ flex \ g++ \ git \ libelf-dev \ zlib1g-dev \ libfl-dev \ systemtap-sdt-dev \ binutils-dev \ libcereal-dev \ llvm-7-dev \ llvm-7-runtime \ libclang-7-dev \ clang-7 \ libgtest-dev \ libgmock-dev \ asciidoctor git clone https://github.com/iovisor/bpftrace mkdir bpftrace/build; cd bpftrace/build; cmake -DCMAKE_BUILD_TYPE=Release .. make -j8 sudo make install ``` 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`. ## Fedora You'll want the newest kernel possible (see kernel requirements), eg, by using Fedora 28 or newer. ``` sudo dnf install -y bison \ flex \ cmake \ make \ git \ gcc-c++ \ elfutils-libelf-devel \ zlib-devel \ llvm-devel \ clang-devel \ bcc-devel \ systemtap-sdt-devel \ binutils-devel \ libbpf-devel \ gtest-devel \ gmock-devel \ cereal-devel \ asciidoctor git clone https://github.com/iovisor/bpftrace cd bpftrace mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release .. make -j8 sudo make install ``` 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`. ## 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/iovisor/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`. ## Using Docker There are currently problems with bpftrace string comparisons when using the Docker build. The regular build is recommended for now. Building inside a Docker container will produce a statically linked bpftrace executable. `./build.sh` There are some more fine-grained options if you find yourself building bpftrace a lot: - `./build-docker-image.sh` - builds just the `bpftrace-builder` Docker image - `./build-debug.sh` - builds bpftrace with debugging information (requires `./build-docker-image.sh` to have already been run) - `./build-release.sh` - builds bpftrace in a release configuration (requires `./build-docker-image.sh` to have already been run) `./build.sh` is equivalent to `./build-docker-image.sh && ./build-release.sh` ## Generic build process Use specific OS build sections listed earlier if available (Ubuntu, Docker). ### Requirements - A C++ compiler - CMake - Flex - Bison - LLVM & Clang 5.0+ development packages - BCC development package - LibElf - Binutils development package - Libcereal - Kernel requirements described earlier ### Compilation ``` git clone https://github.com/iovisor/bpftrace mkdir -p bpftrace/build cd bpftrace/build cmake -DCMAKE_BUILD_TYPE=Release ../ make ``` By default bpftrace will be built as a dynamically linked executable. If a statically linked executable would be preferred and your system has the required libraries installed, the CMake option `-DSTATIC_LINKING:BOOL=ON` can be used. Building bpftrace using the alpine Docker image below will result in a statically linked executable, and additional flags allow for compiling and statically linking the dependencies of bpftrace, see [the embedded build docs](./docs/embedded_builds.md) for more about this type of build. A debug build of bpftrace can be set up with `cmake -DCMAKE_BUILD_TYPE=Debug ../`. The latest version of Google Test will be downloaded on each build. To speed up builds and only download its source on the first run, use the CMake option `-DOFFLINE_BUILDS:BOOL=ON`. To test that the build works, you can try running the test suite, and a one-liner: ``` ./tests/bpftrace_test ./src/bpftrace -e 'kprobe:do_nanosleep { printf("sleep by %s\n", comm); }' ``` # Disable Lockdown From the original patch set description: > This patchset introduces an optional kernel lockdown feature, intended > to strengthen the boundary between UID 0 and the kernel. When enabled, > various pieces of kernel functionality are restricted. Applications that > rely on low-level access to either hardware or the kernel may cease > working as a result - therefore this should not be enabled without > appropriate evaluation beforehand. > > The majority of mainstream distributions have been carrying variants of > this patchset for many years now, so there's value in providing a > doesn't meet every distribution requirement, but gets us much closer to > not requiring external patches. > > - https://patchwork.kernel.org/patch/11140085/ When lockdown is enabled and set to 'confidentiality' all methods that can extract confidential data from the kernel are blocked. This means that: - kprobes are blocked - tracefs access is blocked - probe_read and probe_read_str are blocked which makes it impossible for bpftrace to function. There are a few ways to disable lockdown. 1. Disable secure boot in UEFI. 2. Disable validation using mokutil, run the following command, reboot and follow the prompt. ``` $ sudo mokutil --disable-validation ``` 3. Use the `SysRQ+x` key combination to temporarily lift lockdown (until next boot) Note that you may encounter kernel lockdown error if you install bpftrace via `snap` incorrectly. Please refer to [Ubuntu](#ubuntu-packages) for more details regrading how to use `snap` to install `bpftrace`. bpftrace-0.14.0/LICENSE000066400000000000000000000261361413460502400143760ustar00rootroot00000000000000 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.14.0/README.md000066400000000000000000000210331413460502400146370ustar00rootroot00000000000000# bpftrace [![Build Status](https://github.com/iovisor/bpftrace/workflows/CI/badge.svg?branch=master)](https://github.com/iovisor/bpftrace/actions?query=workflow%3ACI+branch%3Amaster) [![IRC#bpftrace](https://img.shields.io/badge/IRC-bpftrace-blue.svg)](https://webchat.oftc.net/?channels=bpftrace) [![Total alerts](https://img.shields.io/lgtm/alerts/g/iovisor/bpftrace.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/iovisor/bpftrace/alerts/) bpftrace is a high-level tracing language for Linux enhanced Berkeley Packet Filter (eBPF) available in recent Linux kernels (4.x). bpftrace uses LLVM as a backend to compile scripts to BPF-bytecode and makes use of [BCC](https://github.com/iovisor/bcc) for interacting with the Linux BPF system, as well as existing Linux tracing capabilities: kernel dynamic tracing (kprobes), user-level dynamic tracing (uprobes), and tracepoints. The bpftrace language is inspired by awk and C, and predecessor tracers such as DTrace and SystemTap. bpftrace was created by [Alastair Robertson](https://github.com/ajor). To learn more about bpftrace, see the [Manual](man/adoc/bpftrace.adoc) the [Reference Guide](docs/reference_guide.md) and [One-Liner Tutorial](docs/tutorial_one_liners.md). ## One-Liners The following one-liners demonstrate different capabilities: ``` # Files opened by process bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%s %s\n", comm, str(args->filename)); }' # Syscall count by program bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' # Read bytes by process: bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret/ { @[comm] = sum(args->ret); }' # Read size distribution by process: 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 process bpftrace -e 'tracepoint:block:block_rq_issue { printf("%d %s %d\n", pid, comm, args->bytes); }' # Count page faults by process bpftrace -e 'software:faults:1 { @[comm] = count(); }' # Count LLC cache misses by process 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, for processes 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) for examples. ## Install For build and install instructions, see [INSTALL.md](INSTALL.md). ## Tools bpftrace contains various tools, which also serve as examples of programming in the bpftrace language. - tools/[bashreadline.bt](tools/bashreadline.bt): Print entered bash commands system wide. [Examples](tools/bashreadline_example.txt). - tools/[biolatency.bt](tools/biolatency.bt): Block I/O latency as a histogram. [Examples](tools/biolatency_example.txt). - tools/[biosnoop.bt](tools/biosnoop.bt): Block I/O tracing tool, showing per I/O latency. [Examples](tools/biosnoop_example.txt). - tools/[biostacks.bt](tools/biostacks.bt): Show disk I/O latency with initialization stacks. [Examples](tools/biostacks_example.txt). - tools/[bitesize.bt](tools/bitesize.bt): Show disk I/O size as a histogram. [Examples](tools/bitesize_example.txt). - tools/[capable.bt](tools/capable.bt): Trace security capability checks. [Examples](tools/capable_example.txt). - tools/[cpuwalk.bt](tools/cpuwalk.bt): Sample which CPUs are executing processes. [Examples](tools/cpuwalk_example.txt). - tools/[dcsnoop.bt](tools/dcsnoop.bt): Trace directory entry cache (dcache) lookups. [Examples](tools/dcsnoop_example.txt). - tools/[execsnoop.bt](tools/execsnoop.bt): Trace new processes via exec() syscalls. [Examples](tools/execsnoop_example.txt). - tools/[gethostlatency.bt](tools/gethostlatency.bt): Show latency for getaddrinfo/gethostbyname[2] calls. [Examples](tools/gethostlatency_example.txt). - tools/[killsnoop.bt](tools/killsnoop.bt): Trace signals issued by the kill() syscall. [Examples](tools/killsnoop_example.txt). - tools/[loads.bt](tools/loads.bt): Print load averages. [Examples](tools/loads_example.txt). - tools/[mdflush.bt](tools/mdflush.bt): Trace md flush events. [Examples](tools/mdflush_example.txt). - tools/[naptime.bt](tools/naptime.bt): Show voluntary sleep calls. [Examples](tools/naptime_example.txt). - tools/[opensnoop.bt](tools/opensnoop.bt): Trace open() syscalls showing filenames. [Examples](tools/opensnoop_example.txt). - tools/[oomkill.bt](tools/oomkill.bt): Trace OOM killer. [Examples](tools/oomkill_example.txt). - tools/[pidpersec.bt](tools/pidpersec.bt): Count new processes (via fork). [Examples](tools/pidpersec_example.txt). - tools/[runqlat.bt](tools/runqlat.bt): CPU scheduler run queue latency as a histogram. [Examples](tools/runqlat_example.txt). - tools/[runqlen.bt](tools/runqlen.bt): CPU scheduler run queue length as a histogram. [Examples](tools/runqlen_example.txt). - tools/[setuids.bt](tools/setuids.bt): Trace the setuid syscalls: privilege escalation. [Examples](tools/setuids_example.txt). - tools/[statsnoop.bt](tools/statsnoop.bt): Trace stat() syscalls for general debugging. [Examples](tools/statsnoop_example.txt). - tools/[swapin.bt](tools/swapin.bt): Show swapins by process. [Examples](tools/swapin_example.txt). - tools/[syncsnoop.bt](tools/syncsnoop.bt): Trace sync() variety of syscalls. [Examples](tools/syncsnoop_example.txt). - tools/[syscount.bt](tools/syscount.bt): Count system calls. [Examples](tools/syscount_example.txt). - tools/[tcpaccept.bt](tools/tcpaccept.bt): Trace TCP passive connections (accept()). [Examples](tools/tcpaccept_example.txt). - tools/[tcpconnect.bt](tools/tcpconnect.bt): Trace TCP active connections (connect()). [Examples](tools/tcpconnect_example.txt). - tools/[tcpdrop.bt](tools/tcpdrop.bt): Trace kernel-based TCP packet drops with details. [Examples](tools/tcpdrop_example.txt). - tools/[tcplife.bt](tools/tcplife.bt): Trace TCP session lifespans with connection details. [Examples](tools/tcplife_example.txt). - tools/[tcpretrans.bt](tools/tcpretrans.bt): Trace TCP retransmits. [Examples](tools/tcpretrans_example.txt). - tools/[tcpsynbl.bt](tools/tcpsynbl.bt): Show TCP SYN backlog as a histogram. [Examples](tools/tcpsynbl_example.txt). - tools/[threadsnoop.bt](tools/threadsnoop.bt): List new thread creation. [Examples](tools/threadsnoop_example.txt). - tools/[vfscount.bt](tools/vfscount.bt): Count VFS calls. [Examples](tools/vfscount_example.txt). - tools/[vfsstat.bt](tools/vfsstat.bt): Count some VFS calls, with per-second summaries. [Examples](tools/vfsstat_example.txt). - tools/[writeback.bt](tools/writeback.bt): Trace file system writeback events with details. [Examples](tools/writeback_example.txt). - tools/[xfsdist.bt](tools/xfsdist.bt): Summarize XFS operation latency distribution as a histogram. [Examples](tools/xfsdist_example.txt). For more eBPF observability tools, see [bcc tools](https://github.com/iovisor/bcc#tools). ## Probe types
See the [Reference Guide](docs/reference_guide.md) for more detail. ## Support For additional help / discussion, please use our [discussions](https://github.com/iovisor/bpftrace/discussions) page. ## Contributing * Have ideas for new bpftrace tools? [CONTRIBUTING-TOOLS.md](CONTRIBUTING-TOOLS.md) * Bugs reports and feature requests: [Issue Tracker](https://github.com/iovisor/bpftrace/issues) * bpftrace development IRC: #bpftrace at irc.oftc.net ## Development For development and testing a [Vagrantfile](Vagrantfile) is available. Make sure you have the `vbguest` plugin installed, it is required to correctly install the shared file system driver on the ubuntu boxes: ``` $ vagrant plugin install vagrant-vbguest ``` Start VM: ``` $ vagrant status $ vagrant up $YOUR_CHOICE $ vagrant ssh $YOUR_CHOICE ``` ## License Copyright 2019 Alastair Robertson 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.14.0/Vagrantfile000066400000000000000000000074501413460502400155540ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : # Environment variables: # # SKIP_BCC_BUILD: Set to skip the building bcc from source # LLVM_VERSION: The LLVM version to install llvm_version = ENV["LLVM_VERSION"] || 12 $ubuntu_deps = < { 'image' => 'ubuntu/xenial64', 'scripts' => [ $ubuntu_deps, ], }, 'ubuntu-18.04' => { 'image' => 'ubuntu/bionic64', 'scripts' => [ $ubuntu_deps, ], }, 'ubuntu-20.04' => { 'image' => 'ubuntu/focal64', 'scripts' => [ $ubuntu_deps, ], 'fix_console' => 0, }, 'ubuntu-21.04' => { 'image' => 'ubuntu/hirsute64', 'scripts' => [ $ubuntu_deps, ], 'fix_console' => 0, }, 'ubuntu-21.10' => { 'image' => 'ubuntu/impish64', 'scripts' => [ $ubuntu_deps, ], 'fix_console' => 0, }, 'fedora-34' => { 'image' => 'fedora/34-cloud-base', 'scripts' => [ $fedora_deps, ], 'skip_bcc_build' => 1 }, 'centos-8' => { 'image' => 'generic/centos8', 'scripts' => [ $centos_deps, ], }, } boxes.each do | name, params | config.vm.define name do |box| box.vm.box = params['image'] box.vm.provider "virtualbox" do |v| v.memory = 2048 v.cpus = 2 if params['fix_console'] == 1 v.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"] v.customize ["modifyvm", :id, "--uartmode1", "file", "./#{name}_ttyS0.log"] end end box.vm.provider :libvirt do |v| v.memory = 2048 v.cpus = 2 end box.vm.synced_folder ".", "/vagrant", disabled: false (params['scripts'] || []).each do |script| box.vm.provision :shell, inline: script, env: { LLVM_VERSION: llvm_version } end unless ENV['SKIP_BCC_BUILD'] || (params['skip_bcc_build'] == 1) box.vm.provision :shell, privileged: false, inline: $build_bcc end unless ENV['SKIP_LIBBPF_BUILD'] || (params['skip_libbpf_build'] == 1) box.vm.provision :shell, privileged: false, inline: $build_libbpf end config.vm.post_up_message = <<-HEREDOC ####### bpftrace source is available in /vagrant Build command: mkdir build && cd build && cmake /vagrant -DVENDOR_GTEST=1 && make ####### HEREDOC end end end bpftrace-0.14.0/build-debug.sh000077500000000000000000000003121413460502400160770ustar00rootroot00000000000000#!/bin/bash set -e docker run --network host --rm -it -u $(id -u):$(id -g) -v $(pwd):$(pwd) -e STATIC_LINKING=ON -e STATIC_LIBC=ON -e RUN_TESTS=0 bpftrace-builder-alpine "$(pwd)/build-debug" Debug "$@" bpftrace-0.14.0/build-docker-image.sh000077500000000000000000000001631413460502400173440ustar00rootroot00000000000000#!/bin/bash set -e pushd docker docker build --network host -t bpftrace-builder-alpine -f Dockerfile.alpine . popd bpftrace-0.14.0/build-release.sh000077500000000000000000000003501413460502400164330ustar00rootroot00000000000000#!/bin/bash set -e docker run --network host --rm -it -u $(id -u):$(id -g) -v $(pwd):$(pwd) -e STATIC_LINKING=ON -e STATIC_LIBC=ON -e ALLOW_UNSAFE_PROBE=OFF -e RUN_TESTS=0 bpftrace-builder-alpine "$(pwd)/build-release" Release "$@" bpftrace-0.14.0/build.sh000077500000000000000000000001031413460502400150110ustar00rootroot00000000000000#!/bin/bash set -e ./build-docker-image.sh ./build-release.sh "$@" bpftrace-0.14.0/cmake/000077500000000000000000000000001413460502400144415ustar00rootroot00000000000000bpftrace-0.14.0/cmake/FindGMock.cmake000066400000000000000000000107451413460502400172530ustar00rootroot00000000000000# 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.14.0/cmake/FindLibBcc.cmake000066400000000000000000000057221413460502400173700ustar00rootroot00000000000000# - 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_ATTACH_KPROBE_SIX_ARGS_SIGNATURE # LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE # 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) 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 "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)." 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, and libz.a. # If we do a static bpftrace build, we must link them in. find_package(LibBpf) find_package(LibElf) find_package(LibZ) SET(CMAKE_REQUIRED_LIBRARIES ${LIBBCC_BPF_LIBRARIES} ${LIBBPF_LIBRARIES} ${LIBELF_LIBRARIES} ${LIBZ_LIBRARIES}) else() SET(CMAKE_REQUIRED_LIBRARIES ${LIBBCC_LIBRARIES} ${LIBBPF_LIBRARIES}) endif() INCLUDE(CheckCXXSourceCompiles) SET(CMAKE_REQUIRED_INCLUDES ${LIBBCC_INCLUDE_DIRS}) CHECK_CXX_SOURCE_COMPILES(" #include int main(void) { bpf_attach_kprobe(0, BPF_PROBE_ENTRY, \"\", \"\", 0, 0); return 0; } " LIBBCC_ATTACH_KPROBE_SIX_ARGS_SIGNATURE) CHECK_CXX_SOURCE_COMPILES(" #include int main(void) { bpf_attach_uprobe(0, BPF_PROBE_ENTRY, \"\", \"\", 0, 0, 0); return 0; } " LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE) SET(CMAKE_REQUIRED_INCLUDES) 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.14.0/cmake/FindLibBfd.cmake000066400000000000000000000045501413460502400173720ustar00rootroot00000000000000# - 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) # libbfd.so is statically linked with libiberty.a but libbfd.a # is not. So if we do a static bpftrace build, we must link in # libiberty.a find_library (LIBIBERTY_LIBRARIES NAMES libiberty.a PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) 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}) # libbfd.a is not statically linked with libiberty.a or libz.a so we must manually # do it. Furthermore, libbfd uses some libc symbols that we must manually # link against if we're not using static libc (which includes such symbols). if(STATIC_LINKING) find_package(LibZ) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBIBERTY_LIBRARIES} ${LIBZ_LIBRARIES}) if(NOT STATIC_LIBC) set(CMAKE_REQUIRED_FLAGS "-Wl,--start-group -Wl,-Bdynamic -Wl,-Bdynamic -lpthread -Wl,-Bdynamic -ldl") endif() 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) SET(CMAKE_REQUIRED_LIBRARIES) endif() bpftrace-0.14.0/cmake/FindLibBpf.cmake000066400000000000000000000035611413460502400174070ustar00rootroot00000000000000# - 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 #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) 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 "Please install the libbpf development package" LIBBPF_LIBRARIES LIBBPF_INCLUDE_DIRS) mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES) # We need btf_dump support, set LIBBPF_BTF_DUMP_FOUND # when it's found. if (LIBBPF_FOUND) if (KERNEL_INCLUDE_DIRS) set(INCLUDE_KERNEL -isystem ${KERNEL_INCLUDE_DIRS}) endif() include(CheckSymbolExists) # adding also elf for static build check SET(CMAKE_REQUIRED_LIBRARIES ${LIBBPF_LIBRARIES} elf z) # libbpf quirk, needs upstream fix SET(CMAKE_REQUIRED_DEFINITIONS -include stdbool.h ${INCLUDE_KERNEL}) check_symbol_exists(btf_dump__new "${LIBBPF_INCLUDE_DIRS}/bpf/btf.h" HAVE_BTF_DUMP) if (HAVE_BTF_DUMP) set(LIBBPF_BTF_DUMP_FOUND TRUE) endif() check_symbol_exists(btf_dump__emit_type_decl "${LIBBPF_INCLUDE_DIRS}/bpf/btf.h" HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL) check_symbol_exists(bpf_map_lookup_batch "${LIBBPF_INCLUDE_DIRS}/bpf/bpf.h" HAVE_LIBBPF_MAP_BATCH) check_symbol_exists(bpf_link_create "${LIBBPF_INCLUDE_DIRS}/bpf/bpf.h" HAVE_LIBBPF_LINK_CREATE) SET(CMAKE_REQUIRED_DEFINITIONS) SET(CMAKE_REQUIRED_LIBRARIES) endif() bpftrace-0.14.0/cmake/FindLibCereal.cmake000066400000000000000000000007051413460502400200700ustar00rootroot00000000000000# - 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.14.0/cmake/FindLibDw.cmake000066400000000000000000000017121413460502400172460ustar00rootroot00000000000000# - 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.14.0/cmake/FindLibElf.cmake000066400000000000000000000026461413460502400174110ustar00rootroot00000000000000# - 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) SET(CMAKE_REQUIRED_LIBRARIES elf) INCLUDE(CheckCXXSourceCompiles) CHECK_CXX_SOURCE_COMPILES("#include int main() { Elf *e = (Elf*)0; size_t sz; elf_getshdrstrndx(e, &sz); return 0; }" ELF_GETSHDRSTRNDX) SET(CMAKE_REQUIRED_LIBRARIES) mark_as_advanced(LIBELF_INCLUDE_DIRS LIBELF_LIBRARIES ELF_GETSHDRSTRNDX) bpftrace-0.14.0/cmake/FindLibOpcodes.cmake000066400000000000000000000017221413460502400202710ustar00rootroot00000000000000# - Try to find libbfd # Once done this will define # # LIBOPCODES_FOUND - system has libbfd # LIBOPCODES_INCLUDE_DIRS - the libbfd include directory # LIBOPCODES_LIBRARIES - Link these to use libbfd # LIBOPCODES_DEFINITIONS - Compiler switches required for using libbfd #if (LIBOPCODES_LIBRARIES AND LIBOPCODES_INCLUDE_DIRS) # set (LibBpf_FIND_QUIETLY TRUE) #endif (LIBOPCODES_LIBRARIES AND LIBOPCODES_INCLUDE_DIRS) find_path (LIBOPCODES_INCLUDE_DIRS NAMES dis-asm.h PATHS ENV CPATH) find_library (LIBOPCODES_LIBRARIES NAMES opcodes PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBOPCODES_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibOpcodes "Please install the libopcodes development package" LIBOPCODES_LIBRARIES LIBOPCODES_INCLUDE_DIRS) mark_as_advanced(LIBOPCODES_INCLUDE_DIRS LIBOPCODES_LIBRARIES) bpftrace-0.14.0/cmake/FindLibZ.cmake000066400000000000000000000007651413460502400171140ustar00rootroot00000000000000# - 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.14.0/cmake/embed/000077500000000000000000000000001413460502400155155ustar00rootroot00000000000000bpftrace-0.14.0/cmake/embed/embed_clang.cmake000066400000000000000000000106361413460502400207450ustar00rootroot00000000000000if(NOT EMBED_USE_LLVM) return() endif() include(embed_helpers) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(EMBEDDED_BUILD_TYPE "RelWithDebInfo") elseif(CMAKE_BUILD_TYPE STREQUAL "Release") set(EMBEDDED_BUILD_TYPE "MinSizeRel") else() set(EMBEDDED_BUILD_TYPE ${CMAKE_BUILD_TYPE}) endif() if(${LLVM_VERSION} VERSION_EQUAL "12.0.0") set(CLANG_DOWNLOAD_URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/clang-${LLVM_VERSION}.src.tar.xz") set(CLANG_URL_CHECKSUM "SHA256=e26e452e91d4542da3ebbf404f024d3e1cbf103f4cd110c26bf0a19621cca9ed") elseif(${LLVM_VERSION} VERSION_EQUAL "8.0.1") set(CLANG_DOWNLOAD_URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/cfe-${LLVM_VERSION}.src.tar.xz") set(CLANG_URL_CHECKSUM "SHA256=70effd69f7a8ab249f66b0a68aba8b08af52aa2ab710dfb8a0fba102685b1646") else() message(FATAL_ERROR "No supported LLVM version has been specified with LLVM_VERSION (${EMBED_LLVM_VERSION}), aborting") endif() ProcessorCount(nproc) set(LIBCLANG_INSTALL_COMMAND "mkdir -p /lib/ && \ cp /lib/libclang.a /lib/libclang.a" ) set(CLANG_INSTALL_COMMAND INSTALL_COMMAND /bin/bash -c "${CMAKE_MAKE_PROGRAM} install -j${nproc} && ${LIBCLANG_INSTALL_COMMAND}" ) set(CLANG_LIBRARY_TARGETS clang clangAST clangAnalysis clangBasic clangDriver clangEdit clangFormat clangFrontend clangIndex clangLex clangParse clangRewrite clangSema clangSerialization clangToolingCore clangToolingInclusions ) if(${EMBED_LLVM_VERSION} VERSION_EQUAL "12") set(CLANG_LIBRARY_TARGETS ${CLANG_LIBRARY_TARGETS} clangAPINotes # 12 clangARCMigrate clangASTMatchers clangCodeGen clangCrossTU clangDependencyScanning #12 clangDirectoryWatcher #12 clangDynamicASTMatchers clangFrontendTool clangHandleCXX clangHandleLLVM clangIndexSerialization # 12 clangRewriteFrontend clangStaticAnalyzerCheckers clangStaticAnalyzerCore clangStaticAnalyzerFrontend clangTesting clangTooling clangToolingASTDiff clangToolingRefactoring clangToolingSyntax clangTransformer ) endif() # These configure flags are a blending of the Alpine, debian, and gentoo # packages configure flags, customized to reduce build targets as much as # possible set(CLANG_CONFIGURE_FLAGS -Wno-dev -DLLVM_TARGETS_TO_BUILD=BPF -DCMAKE_BUILD_TYPE=${EMBEDDED_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX= -DCMAKE_VERBOSE_MAKEFILE=OFF -DCLANG_VENDOR=bpftrace -DCLANG_BUILD_EXAMPLES=OFF -DCLANG_INCLUDE_DOCS=OFF -DCLANG_INCLUDE_TESTS=OFF -DCLANG_PLUGIN_SUPPORT=ON -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_EH=ON -DLLVM_ENABLE_RTTI=ON -DCLANG_BUILD_TOOLS=OFF -DLLVM_DIR=${EMBEDDED_LLVM_INSTALL_DIR}/lib/cmake/llvm ) if(EMBED_BUILD_LLVM) set(CLANG_TARGET_LIBS "") foreach(clang_target IN LISTS CLANG_LIBRARY_TARGETS) list(APPEND CLANG_TARGET_LIBS "/lib/lib${clang_target}.a") endforeach(clang_target) ExternalProject_Add(embedded_clang URL "${CLANG_DOWNLOAD_URL}" URL_HASH "${CLANG_URL_CHECKSUM}" CMAKE_ARGS "${CLANG_CONFIGURE_FLAGS}" ${CLANG_PATCH_COMMAND} ${CLANG_BUILD_COMMAND} ${CLANG_INSTALL_COMMAND} BUILD_BYPRODUCTS ${CLANG_TARGET_LIBS} UPDATE_DISCONNECTED 1 DOWNLOAD_NO_PROGRESS 1 ) ExternalProject_Add_StepDependencies(embedded_clang install embedded_llvm) # Set up library targets and locations ExternalProject_Get_Property(embedded_clang INSTALL_DIR) set(EMBEDDED_CLANG_INSTALL_DIR "${INSTALL_DIR}/lib") else() set(EMBEDDED_CLANG_INSTALL_DIR "${EMBED_LLVM_PATH}") endif(EMBED_BUILD_LLVM) set(CLANG_EMBEDDED_CMAKE_TARGETS "") set(CLANG_EMBEDDED_CMAKE_LIBS "") include_directories(SYSTEM ${EMBEDDED_CLANG_INSTALL_DIR}/include) foreach(clang_target IN LISTS CLANG_LIBRARY_TARGETS) # Special handling is needed to not overlap with the library definition from # system cmake files for Clang's "clang" target. list(APPEND CLANG_EMBEDDED_CMAKE_TARGETS ${clang_target}) add_library(${clang_target} STATIC IMPORTED) set_property( TARGET ${clang_target} PROPERTY IMPORTED_LOCATION "${EMBEDDED_CLANG_INSTALL_DIR}/lib${clang_target}.a" ) if(EMBED_BUILD_LLVM) add_dependencies(${clang_target} embedded_clang) endif(EMBED_BUILD_LLVM) endforeach(clang_target) bpftrace-0.14.0/cmake/embed/embed_helpers.cmake000066400000000000000000000074741413460502400213310ustar00rootroot00000000000000include(ExternalProject) include(ProcessorCount) # Workaround to remove dynamic libs from library dependencies function(unlink_transitive_dependency targets dep_to_remove) foreach(tgt ${targets}) 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) # Detect the distribution bpftrace is being built on function(detect_host_os os_id) file(STRINGS "/etc/os-release" HOST_OS_INFO) foreach(os_info IN LISTS HOST_OS_INFO) if(os_info MATCHES "^ID=") string(REPLACE "ID=" "" HOST_OS_ID ${os_info}) set(${os_id} ${HOST_OS_ID} PARENT_SCOPE) break() endif() endforeach(os_info) endfunction(detect_host_os os_id) function(detect_host_os_family family_id) file(STRINGS "/etc/os-release" HOST_OS_INFO) foreach(os_info IN LISTS HOST_OS_INFO) if(os_info MATCHES "^ID_LIKE=") string(REPLACE "ID_LIKE=" "" HOST_OS_ID_LIKE ${os_info}) set(${family_id} ${HOST_OS_ID_LIKE} PARENT_SCOPE) break() endif() endforeach(os_info) endfunction(detect_host_os_family family_id) # TODO dalehamel # DRY up get_host_triple and get_target_triple by accepting a triple_type arg # For simplicity sake, kept separate for now. function(get_host_triple out) # Get the architecture. set(arch ${CMAKE_HOST_SYSTEM_PROCESSOR}) # Get os and vendor if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux") set(vendor "generic") set(os "linux") else() message(AUTHOR_WARNING "The host system ${CMAKE_HOST_SYSTEM_NAME} isn't supported") endif() set(triple "${arch}-${vendor}-${os}") set(${out} ${triple} PARENT_SCOPE) message(STATUS "Detected host triple: ${triple}") endfunction() function(get_target_triple out) # Get the architecture. set(arch ${CMAKE_SYSTEM_PROCESSOR}) # Get os and vendor if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(vendor "generic") set(os "linux") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android") set(vendor "android") set(os "linux") message(AUTHOR_WARNING "Android build not yet fully implemented.") else() message(AUTHOR_WARNING "The target system ${CMAKE_SYSTEM_NAME} isn't supported") endif() set(triple "${arch}-${vendor}-${os}") set(${out} ${triple} PARENT_SCOPE) message(STATUS "Detected target triple: ${triple}") endfunction() function(fix_llvm_linkflags targetProperty propertyValue) set_target_properties(${target_property} PROPERTIES INTERFACE_LINK_LIBRARIES "${propertyValue}" ) endfunction(fix_llvm_linkflags targetProperty propertyValue) function(prepare_patch_series patchSeries patchPath) message("Writing patch series to ${patchPath}/series ...") file(WRITE "${patchPath}/series" "") foreach(patch_info IN ITEMS ${patchSeries}) file(APPEND "${patchPath}/series" "${patch_info}\n") endforeach(patch_info) endfunction(prepare_patch_series patchSeries patchPath) function(fetch_patches patchName patchPath patchURL patchChecksum stripLevel) if(NOT EXISTS "${patchPath}/${patchName}") message("Downloading ${patchURL}") file(MAKE_DIRECTORY ${patchPath}) file(DOWNLOAD "${patchURL}" "${patchPath}/${patchName}" EXPECTED_HASH SHA256=${patchChecksum}) # Can add to this if ladder to support additional patch formats, tar # probably catches quit a lot... if(patchName MATCHES .*tar.*) execute_process(COMMAND tar -xpf ${patchPath}/${patchName} --strip-components=${stripLevel} -C ${patchPath}) else() message("Patch ${patchName} doesn't appear to a tar achive, assuming it is a plaintext patch") endif() endif() endfunction(fetch_patches patchName patchPatch patchURL patchChecksum) bpftrace-0.14.0/cmake/embed/embed_llvm.cmake000066400000000000000000000124041413460502400206260ustar00rootroot00000000000000if(NOT EMBED_USE_LLVM) return() endif() include(embed_helpers) # TO DO # Set up cross-compilation # https://cmake.org/cmake/help/v3.6/manual/cmake-toolchains.7.html#cross-compiling-using-clang get_host_triple(CHOST) get_target_triple(CBUILD) if(CMAKE_BUILD_TYPE STREQUAL "Debug") # Same as debian, see # https://salsa.debian.org/pkg-llvm-team/llvm-toolchain/blob/8/debian/rules set(EMBEDDED_BUILD_TYPE "RelWithDebInfo") elseif(CMAKE_BUILD_TYPE STREQUAL "Release") set(EMBEDDED_BUILD_TYPE "MinSizeRel") else() set(EMBEDDED_BUILD_TYPE ${CMAKE_BUILD_TYPE}) endif() if(${EMBED_LLVM_VERSION} VERSION_EQUAL "12") set(LLVM_FULL_VERSION "12.0.0") set(LLVM_VERSION ${LLVM_FULL_VERSION}) set(LLVM_VERSION_MAJOR "12") set(LLVM_VERSION_MINOR "0") set(LLVM_VERSION_PATCH "0") set(LLVM_URL_CHECKSUM "SHA256=49dc47c8697a1a0abd4ee51629a696d7bfe803662f2a7252a3b16fc75f3a8b50") elseif(${EMBED_LLVM_VERSION} VERSION_EQUAL "8") set(LLVM_FULL_VERSION "8.0.1") set(LLVM_VERSION ${LLVM_FULL_VERSION}) set(LLVM_VERSION_MAJOR "8") set(LLVM_VERSION_MINOR "0") set(LLVM_VERSION_PATCH "1") set(LLVM_URL_CHECKSUM "SHA256=44787a6d02f7140f145e2250d56c9f849334e11f9ae379827510ed72f12b75e7") else() message(FATAL_ERROR "No supported LLVM version has been specified with LLVM_VERSION (${EMBED_LLVM_VERSION}), aborting") endif() set(LLVM_DOWNLOAD_URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_FULL_VERSION}/llvm-${LLVM_FULL_VERSION}.src.tar.xz") # Default to building almost all targets, + BPF specific ones set(LLVM_LIBRARY_TARGETS LLVMAggressiveInstCombine LLVMAnalysis LLVMAsmParser LLVMAsmPrinter LLVMBinaryFormat LLVMBitReader LLVMBitWriter LLVMBPFAsmParser LLVMBPFCodeGen LLVMBPFDesc LLVMBPFDisassembler LLVMBPFInfo LLVMCodeGen LLVMCore LLVMCoroutines LLVMCoverage LLVMDebugInfoCodeView LLVMDebugInfoDWARF LLVMDebugInfoMSF LLVMDebugInfoPDB LLVMDemangle LLVMDlltoolDriver LLVMExecutionEngine LLVMFuzzMutate LLVMGlobalISel LLVMInstCombine LLVMInstrumentation LLVMInterpreter LLVMipo LLVMIRReader LLVMLibDriver LLVMLineEditor LLVMLinker LLVMLTO LLVMMC LLVMMCA LLVMMCDisassembler LLVMMCJIT LLVMMCParser LLVMMIRParser LLVMObjCARCOpts LLVMObject LLVMObjectYAML LLVMOption LLVMOrcJIT LLVMPasses LLVMProfileData LLVMRuntimeDyld LLVMScalarOpts LLVMSelectionDAG LLVMSymbolize LLVMTableGen LLVMTarget LLVMTextAPI LLVMTransformUtils LLVMVectorize LLVMWindowsManifest LLVMXRay LLVMSupport ) if(${EMBED_LLVM_VERSION} VERSION_EQUAL "8") set(LLVM_LIBRARY_TARGETS ${LLVM_LIBRARY_TARGETS} LLVMBPFAsmPrinter LLVMOptRemarks ) endif() if(${EMBED_LLVM_VERSION} VERSION_EQUAL "12") set(LLVM_LIBRARY_TARGETS ${LLVM_LIBRARY_TARGETS} LLVMBitstreamReader LLVMCFGuard LLVMDWARFLinker LLVMDebugInfoGSYM LLVMExtensions LLVMFileCheck LLVMFrontendOpenACC LLVMFrontendOpenMP LLVMHelloNew LLVMInterfaceStub LLVMJITLink LLVMOrcShared LLVMOrcTargetProcess LLVMRemarks ) endif() # These build flags are based off of Alpine, Debian and Gentoo packages # optimized for compatibility and reducing build targets set(LLVM_CONFIGURE_FLAGS -Wno-dev -DLLVM_TARGETS_TO_BUILD=BPF -DCMAKE_BUILD_TYPE=${EMBEDDED_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX= -DLLVM_BINUTILS_INCDIR=/usr/include -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_EXTERNAL_COMPILER_RT=ON -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_BUILD_TESTS=OFF -DLLVM_DEFAULT_TARGET_TRIPLE=${CBUILD} -DLLVM_ENABLE_ASSERTIONS=OFF -DLLVM_ENABLE_CXX1Y=ON -DLLVM_ENABLE_FFI=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_LIBCXX=OFF -DLLVM_ENABLE_PIC=ON -DLLVM_ENABLE_LIBPFM=OFF -DLLVM_ENABLE_EH=ON -DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_SPHINX=OFF -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=ON -DLLVM_HOST_TRIPLE=${CHOST} -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_APPEND_VC_REV=OFF ) if(EMBED_BUILD_LLVM) set(LLVM_TARGET_LIBS "") foreach(llvm_target IN LISTS LLVM_LIBRARY_TARGETS) list(APPEND LLVM_TARGET_LIBS "/lib/lib${llvm_target}.a") endforeach(llvm_target) ExternalProject_Add(embedded_llvm URL "${LLVM_DOWNLOAD_URL}" URL_HASH "${LLVM_URL_CHECKSUM}" CMAKE_ARGS "${LLVM_CONFIGURE_FLAGS}" BUILD_BYPRODUCTS ${LLVM_TARGET_LIBS} UPDATE_DISCONNECTED 1 DOWNLOAD_NO_PROGRESS 1 ) # Set up build targets and map to embedded paths ExternalProject_Get_Property(embedded_llvm INSTALL_DIR) set(EMBEDDED_LLVM_INSTALL_DIR "${INSTALL_DIR}/lib") else() set(EMBEDDED_LLVM_INSTALL_DIR "${EMBED_LLVM_PATH}") endif() set(LLVM_EMBEDDED_CMAKE_TARGETS "") include_directories(SYSTEM ${EMBEDDED_LLVM_INSTALL_DIR}/include) foreach(llvm_target IN LISTS LLVM_LIBRARY_TARGETS) list(APPEND LLVM_EMBEDDED_CMAKE_TARGETS ${llvm_target}) add_library(${llvm_target} STATIC IMPORTED) set_property( TARGET ${llvm_target} PROPERTY IMPORTED_LOCATION "${EMBEDDED_LLVM_INSTALL_DIR}/lib${llvm_target}.a" ) if(EMBED_BUILD_LLVM) add_dependencies(${llvm_target} embedded_llvm) endif() endforeach(llvm_target) bpftrace-0.14.0/docker/000077500000000000000000000000001413460502400146305ustar00rootroot00000000000000bpftrace-0.14.0/docker/Dockerfile.alpine000066400000000000000000000015671413460502400201020ustar00rootroot00000000000000ARG ALPINE_VERSION=3.11 FROM alpine:${ALPINE_VERSION} ARG LLVM_VERSION="9" ARG CEREAL_VERSION=1.3.0 RUN apk add --update \ asciidoctor \ bash \ bison \ bcc-dev \ bcc-static \ libbpf-dev \ build-base \ clang-dev \ clang-static \ cmake \ elfutils-dev \ flex-dev \ git \ gtest-dev \ libc6-compat \ linux-headers \ llvm${LLVM_VERSION}-dev \ llvm${LLVM_VERSION}-static \ python3 \ wget \ zlib-dev \ zlib-static WORKDIR / RUN mv /usr/lib/libbccbpf.a /usr/lib/libbcc_bpf.a && \ ln -s $(which python3) /usr/bin/python && \ ln -s /lib /lib/x86_64-linux-gnu RUN wget https://github.com/USCiLab/cereal/archive/refs/tags/v${CEREAL_VERSION}.tar.gz && \ tar xf v${CEREAL_VERSION}.tar.gz && \ cp -r cereal-${CEREAL_VERSION}/include/cereal /usr/include COPY build.sh /build.sh RUN chmod 755 /build.sh ENTRYPOINT ["/bin/sh", "/build.sh"] bpftrace-0.14.0/docker/Dockerfile.bionic000066400000000000000000000033561413460502400200730ustar00rootroot00000000000000FROM ubuntu:bionic ARG LLVM_VERSION ENV LLVM_VERSION=$LLVM_VERSION RUN apt-get update && apt-get install -y curl gnupg &&\ llvmRepository="\n\ deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main\n\ deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main\n\ deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-${LLVM_VERSION} main\n\ deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-${LLVM_VERSION} main\n" &&\ echo $llvmRepository >> /etc/apt/sources.list && \ curl -L https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD && \ echo "deb https://repo.iovisor.org/apt/bionic bionic main" | tee /etc/apt/sources.list.d/iovisor.list RUN curl -L --output /tmp/cmake.tar.gz \ https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0-linux-x86_64.tar.gz \ && tar -xf /tmp/cmake.tar.gz -C /usr/local/ --strip-components=1 RUN apt-get update && apt-get install -y \ make \ pkg-config \ asciidoctor \ bison \ binutils-dev \ flex \ g++-8 \ git \ libelf-dev \ zlib1g-dev \ libbcc \ libcereal-dev \ libdw-dev \ clang-${LLVM_VERSION} \ libclang-${LLVM_VERSION}-dev \ libclang-common-${LLVM_VERSION}-dev \ libclang1-${LLVM_VERSION} \ llvm-${LLVM_VERSION} \ llvm-${LLVM_VERSION}-dev \ llvm-${LLVM_VERSION}-runtime \ libllvm${LLVM_VERSION} \ systemtap-sdt-dev \ python3 \ xxd RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 \ --slave /usr/bin/g++ g++ /usr/bin/g++-8 COPY build.sh /build.sh RUN chmod 755 /build.sh ENTRYPOINT ["bash", "/build.sh"] bpftrace-0.14.0/docker/Dockerfile.fedora30000066400000000000000000000006511413460502400202260ustar00rootroot00000000000000FROM fedora:30 RUN dnf install -y \ asciidoctor \ bison \ binutils-devel \ cereal-devel \ clang-devel \ cmake \ elfutils-libelf-devel \ elfutils-libs \ flex \ gcc-c++ \ git \ gtest-devel \ gmock-devel \ llvm-devel \ make \ zlib-devel \ bcc-devel \ systemtap-sdt-devel COPY build.sh /build.sh RUN chmod 755 /build.sh ENTRYPOINT ["/bin/sh", "/build.sh"] bpftrace-0.14.0/docker/Dockerfile.llvm000066400000000000000000000027051413460502400175770ustar00rootroot00000000000000ARG BASE=bionic FROM ubuntu:${BASE} ARG BASE ARG LLVM_VERSION ARG BUILD_TYPE="Release" ENV LLVM_VERSION=${LLVM_VERSION} ENV BASE=${BASE} ENV BUILD_TYPE=${BUILD_TYPE} COPY cmake /build/llvm/cmake COPY CMakeLists-LLVM.txt /build/llvm/CMakeLists.txt RUN if [ "${BASE}" = "xenial" ]; then \ apt update \ && apt install -y software-properties-common \ && add-apt-repository -y ppa:ubuntu-toolchain-r/test \ ; fi RUN apt update \ && apt install -y --no-install-recommends \ ca-certificates \ curl \ g++-8 \ gcc-8 \ gcc-8-plugin-dev \ make \ python \ python3 \ rsync \ tar \ && update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 \ --slave /usr/bin/g++ g++ /usr/bin/g++-8 \ && cp /usr/lib/gcc/x86_64-linux-gnu/8/plugin/include/plugin-api.h /usr/local/include RUN curl -L --output /tmp/cmake.tar.gz \ https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0-linux-x86_64.tar.gz \ && tar -xf /tmp/cmake.tar.gz -C /usr/local/ --strip-components=1 RUN cd /build/llvm \ && cmake . -DLLVM_VERSION=${LLVM_VERSION} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ && make embedded_llvm -j$(nproc) \ && make embedded_clang -j$(nproc) \ && rm -rf embedded_llvm-prefix/src embedded_clang-prefix/src \ && rm -rf embedded_llvm-prefix/tmp embedded_clang-prefix/tmp \ && rsync -a embedded_clang-prefix/ embedded_llvm-prefix/ /usr/local \ && rm -rf /build/llvm bpftrace-0.14.0/docker/Dockerfile.release000066400000000000000000000003611413460502400202410ustar00rootroot00000000000000ARG BASE=focal FROM ubuntu:$BASE ARG build_dir=build-embedded # Run security updates RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* COPY /$build_dir/src/bpftrace /usr/bin/bpftrace COPY /tools/*.bt /usr/local/bin/ bpftrace-0.14.0/docker/Dockerfile.ubuntu-glibc000066400000000000000000000032731413460502400212260ustar00rootroot00000000000000ARG BASE="bionic" ARG LLVM_VERSION="8" FROM quay.io/iovisor/bpftrace-llvm:${BASE}_${LLVM_VERSION} ARG BASE ARG LLVM_VERSION ARG CMAKE_VER="3.16" ARG CMAKE_BUILD="2" ARG bcc_ref="v0.22.0" ARG bcc_org="iovisor" ARG libbpf_ref="092a606856" ENV LLVM_VERSION=$LLVM_VERSION ENV CMAKE_VER=${CMAKE_VER} ENV CMAKE_BUILD="${CMAKE_BUILD}" ENV BASE=${BASE} RUN apt-get update \ && apt-get install -y \ asciidoctor \ bison \ binutils-dev \ flex \ git \ libelf-dev \ zlib1g-dev \ libiberty-dev \ libbfd-dev \ libcereal-dev \ libedit-dev \ systemtap-sdt-dev \ python3 \ quilt \ && apt-get install --no-install-recommends -y \ pkg-config RUN mkdir -p /src \ && git clone https://github.com/$bcc_org/bcc /src/bcc \ && cd /src/bcc \ && git checkout $bcc_ref \ && git submodule update \ && mkdir build \ && cd build \ && cmake .. \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DENABLE_EXAMPLES=0 -DENABLE_MAN=0 -DENABLE_TESTS=0 \ -DENABLE_LLVM_NATIVECODEGEN=0 \ && make -j$(nproc) \ && make install \ && mkdir -p /usr/local/lib \ && cp src/cc/libbcc.a /usr/local/lib/libbcc.a \ && cp src/cc/libbcc-loader-static.a /usr/local/lib/libbcc-loader-static.a \ && cp ./src/cc/libbcc_bpf.a /usr/local/lib/libbpf.a \ && cp ./src/cc/libbcc_bpf.a /usr/local/lib/libbcc_bpf.a RUN git clone https://github.com/libbpf/libbpf.git /src/libbpf \ && cd /src/libbpf/src \ && git checkout $libbpf_ref \ && CC=gcc make -j$(nproc) install install_uapi_headers COPY build.sh /build.sh RUN chmod 755 /build.sh ENTRYPOINT ["bash", "/build.sh"] bpftrace-0.14.0/docker/build.sh000077500000000000000000000040721413460502400162710ustar00rootroot00000000000000#!/bin/bash set -e WARNINGS_AS_ERRORS=${WARNINGS_AS_ERRORS:-OFF} STATIC_LINKING=${STATIC_LINKING:-OFF} STATIC_LIBC=${STATIC_LIBC:-OFF} LLVM_VERSION=${LLVM_VERSION:-8} # default llvm to latest version EMBED_USE_LLVM=${EMBED_USE_LLVM:-OFF} EMBED_BUILD_LLVM=${EMBED_BUILD_LLVM:-OFF} ALLOW_UNSAFE_PROBE=${ALLOW_UNSAFE_PROBE:-OFF} DEPS_ONLY=${DEPS_ONLY:-OFF} RUN_TESTS=${RUN_TESTS:-1} RUN_MEMLEAK_TEST=${RUN_MEMLEAK_TEST:-0} VENDOR_GTEST=${VENDOR_GTEST:-OFF} CI_TIMEOUT=${CI_TIMEOUT:-0} BUILD_LIBBPF=${BUILD_LIBBPF:-OFF} CC=${CC:cc} CXX=${CXX:c++} if [[ $LLVM_VERSION -eq 13 ]]; then touch /usr/lib/llvm-13/bin/llvm-omp-device-info fi if [[ $BUILD_LIBBPF = ON ]]; then mkdir /src git clone https://github.com/libbpf/libbpf.git /src/libbpf cd /src/libbpf/src CC=gcc make -j$(nproc) # libbpf defaults to /usr/lib64 which doesn't work on debian like systems # this should work on both PREFIX=/usr/local/ LIBDIR=/usr/local/lib make install install_uapi_headers fi # Build bpftrace mkdir -p "$1" cd "$1" cmake -DCMAKE_BUILD_TYPE="$2" \ -DWARNINGS_AS_ERRORS:BOOL=$WARNINGS_AS_ERRORS \ -DSTATIC_LINKING:BOOL=$STATIC_LINKING \ -DSTATIC_LIBC:BOOL=$STATIC_LIBC \ -DEMBED_USE_LLVM:BOOL=$EMBED_USE_LLVM \ -DEMBED_BUILD_LLVM:BOOL=$EMBED_BUILD_LLVM \ -DEMBED_LLVM_VERSION=$LLVM_VERSION \ -DALLOW_UNSAFE_PROBE:BOOL=$ALLOW_UNSAFE_PROBE \ -DVENDOR_GTEST=$VENDOR_GTEST \ -DBUILD_ASAN:BOOL=$RUN_MEMLEAK_TEST \ -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ "${CMAKE_EXTRA_FLAGS}" \ ../ shift 2 # It is necessary to build embedded llvm and clang targets first, # so that their headers can be referenced [[ $DEPS_ONLY == "ON" ]] && exit 0 make "$@" -j $(nproc) if [ $RUN_TESTS = 1 ]; then if [ "$RUN_ALL_TESTS" = "1" ]; then ctest -V --exclude-regex "$TEST_GROUPS_DISABLE" else ./tests/bpftrace_test $TEST_ARGS; fi fi # Memleak tests require bpftrace built with -fsanitize=address so it cannot be # usually run with unit/runtime tests (RUN_TESTS should be set to 0). if [ $RUN_MEMLEAK_TEST = 1 ]; then ./tests/memleak-tests.sh fi bpftrace-0.14.0/docker/scripts/000077500000000000000000000000001413460502400163175ustar00rootroot00000000000000bpftrace-0.14.0/docker/scripts/auth.sh000077500000000000000000000013401413460502400176150ustar00rootroot00000000000000#!/bin/bash set -e # For now only quay.io is supported, but this could be portable to dockerhub # and other image repositories. # Forks can push using this approach if they create a quay.io bot user # with name matching of ORGNAME+bpftrace_buildbot, or by setting QUAY_BOT_NAME git_repo=$1 # github.repository format: ORGNAME/REPONAME # Set this value as QUAY_TOKEN in the github repository settings "Secrets" tab [[ -z "${QUAY_TOKEN}" ]] && echo "QUAY_TOKEN not set" && exit 0 # Set this to match the name of the bot user on quay.io [[ -z "${QUAY_BOT_NAME}" ]] && QUAY_BOT_NAME="bpftrace_buildbot" quay_user="$(dirname ${git_repo})+${QUAY_BOT_NAME}" echo "${QUAY_TOKEN}" | docker login -u="${quay_user}" --password-stdin quay.io bpftrace-0.14.0/docker/scripts/push.sh000077500000000000000000000057071413460502400176460ustar00rootroot00000000000000#!/bin/bash set -e # Push docker tags to a configured docker repo, defaulting to quay.io # You must run login.sh before running this script. DEFAULT_DOCKER_REPO="quay.io" DEFAULT_RELEASE_TARGET="vanilla_llvm12+clang+glibc2.27" # Currently only support pushing to quay.io DOCKER_REPO=${DEFAULT_DOCKER_REPO} git_repo=$1 # github.repository format: ORGNAME/REPONAME git_ref=$2 # github.ref format: refs/REMOTE/REF # eg, refs/heads/BRANCH # refs/tags/v0.9.6-pre git_sha=$3 # github.sha GIT_SHA type_name=$4 # build name, s/+/_/g eg, vanilla_llvm+clang+glibc2.27 edge=${5:-OFF} # bool eg, ON # edge builds are meant to trace BCC between releases, and provide the newest # feature sets possible, with the possibility of being less stable. # refname will be either a branch like "master" or "some-branch", # or a tag, like "v0.9.6-pre". # When a tag is pushed, a build is done for both the branch and the tag, as # separate builds. # This is a feature specific #to github actions based on the `github.ref` object refname=$(basename ${git_ref}) # The build type needs to be sanitized into a valid tag, replacing + with _ type_tag="$(echo ${type_name} | sed 's/+/_/g')" # The main docker image build, copying the bpftrace artifact on top of a vanilla OS image echo "Building release docker image" docker build -t ${DOCKER_REPO}/${git_repo}:${git_sha}-${type_tag} -f docker/Dockerfile.release . echo "Upload image for git sha ${git_sha} to ${DOCKER_REPO}/${git_repo}" docker push ${DOCKER_REPO}/${git_repo}:${git_sha}-${type_tag} echo "Push tags to branch or git tag HEAD refs" docker tag ${DOCKER_REPO}/${git_repo}:${git_sha}-${type_tag} ${DOCKER_REPO}/${git_repo}:${refname}-${type_tag} docker push ${DOCKER_REPO}/${git_repo}:${refname}-${type_tag} # Only push to un-suffixed tags for the default release target build type if [[ "${type_name}" == "${DEFAULT_RELEASE_TARGET}"* ]];then # Update branch / git tag ref echo "Pushing tags for ${DOCKER_REPO}/${git_repo}:${refname}" docker tag ${DOCKER_REPO}/${git_repo}:${git_sha}-${type_tag} ${DOCKER_REPO}/${git_repo}:${refname} docker push ${DOCKER_REPO}/${git_repo}:${refname} if [[ "${refname}" == "master" ]];then if [[ "${edge}" == "ON" ]];then echo "This is an edge build on master, pushing ${DOCKER_REPO}/${git_repo}:edge" docker tag ${DOCKER_REPO}/${git_repo}:${git_sha}-${type_tag} ${DOCKER_REPO}/${git_repo}:edge docker push ${DOCKER_REPO}/${git_repo}:edge else echo "This is a build on master, pushing ${DOCKER_REPO}/${git_repo}:latest :SHA as well" docker tag ${DOCKER_REPO}/${git_repo}:${git_sha}-${type_tag} ${DOCKER_REPO}/${git_repo}:latest docker tag ${DOCKER_REPO}/${git_repo}:${git_sha}-${type_tag} ${DOCKER_REPO}/${git_repo}:${git_sha} docker push ${DOCKER_REPO}/${git_repo}:latest docker push ${DOCKER_REPO}/${git_repo}:${git_sha} fi fi fi bpftrace-0.14.0/docs/000077500000000000000000000000001413460502400143115ustar00rootroot00000000000000bpftrace-0.14.0/docs/developers.md000066400000000000000000000030011413460502400167750ustar00rootroot00000000000000# bpftrace development guide ## Code style We use clang-format with our custom config for formatting code. This was [introduced](https://github.com/iovisor/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/master/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. ## 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-0.14.0/docs/embedded_builds.md000066400000000000000000000204451413460502400177330ustar00rootroot00000000000000# Embedding dependencies To make bpftrace more portable, it has long supported an alpine-based musl build, which statically compiled bpftrace resulting in no runtime linking required. The drawback to this approach is that LLVM libraries, even when statically compiled, depends on symbols from libdl, and works best and most predictably when dynamically linked to libc. To embed everything except for libc, building LLVM and Clang from source is supported. This allows for linking to arbitrary libc targets dynamically, which may provide the best of both worlds between a purely static and a purely dynamically-linked bpftrace executable. For this reason, there is CMake support in the bpftrace project to build LLVM and Clang from source, as these are the heaviest dependencies of bpftrace. Other library dependencies can be obtained by most package managers reliably. Upstream packages provided by package maintainers can't be depended on to present all of the necessary built artifacts to statically link against LLVM and Clang, despite the project [LLVM catering to this with its own CMake build process](https://llvm.org/docs/CMake.html#embedding-llvm-in-your-project). # Configuration flags To make an semi-static executable that includes everything except libc, the CMake option `STATIC_LIBC:BOOL=OFF` can be added. This should allow for a bpftrace executable that can run on any system with a compatible libc. To assist in this, new configuration flags allow to download, configure, build and embed static libraries for all dependencies of bpftrace, rather than relying on system libraries. To build the necessary static LLVM or Clang libraries, `-DEMBED_LLVM:BOOL=ON` and `-DEMBED_CLANG=ON` can be used respectively. Clang can link to system LLVM as well, but may need to be patched. It is possible to link bpftrace's embedded libraries with system libraries, so linking to a distribution specific LLVM is possible. Many distributions do not include `libclang.a`, using `-DEMBED_LIBCLANG_ONLY=ON` will build only `libclang.a`, allowing for linking to system LLVM and Clang libraries on Debian, as opposed to Vanilla LLVM sources. # Build times To build from scratch on a modern development laptop with an 8-way parallel build, bpftrace and its dependencies can be built in about 30 minutes. Subsequent builds will not have this overhead, once the embedded dependencies are built and cached, and will proceed at the comparable speeds as dynamically linked builds. ## CI environments Internal CI environments with any sort of caching mechanism should be able to just cache any external project's build directories, and the overhead of building on CI shouldn't be any different than other CI jobs. # Embedding clang On the alpine platform, libclang.a is generated by default and a static build is already achieved using this distribution. This libclang dependency is only achievable with custom builds, or by using alpine. To get around this problem, the `embed_clang.cmake` file provides the necessary static libraries for bpftrace to link against, by downloading LLVM from a github tagged release and compiling it directly using custom cmake flags, based on those provided by alpine already. This basically pulls the fairly simple alpine-build "in-tree", using cmake flags based on it, and specifically itemizing the libraries that will be link-time dependencies of bpftrace. In turn, these clang libraries depend heavily on LLVM libraries, so this necessitates having access to LLVM static libraries as well. It is also possible to build only libclang from (mostly) vanilla sources, and use system LLVM and Clang libraries. This is what the `EMBED_LIBCLANG_ONLY` flag provides. # Embedding LLVM It is possible to avoid embedding LLVM by applying distribution specific patches to embedded clang, so that it will be patch-compatible with the LLVM libraries shipped with the target system. This can save time by avoiding building LLVM altogether, but as distributions LLVM libraries may have more dependencies and other bloat than the embedded LLVM, this can result in a larger executable. On Ubuntu, the size of the bpftrace executable is increased from 36MB to 75MB (as of Jan 13 2020). This is still a desirable time-saver where space doesn't matter, such as when doing Debug builds - it can avoid building 15GB of LLVM libraries. ## Distribution patches The LLVM builds maintained by distributions have patches on top of the tagged releases. It may be favorable from time-to-time to pull in these patches. By downloading patches from an external source and writing a custom series file, `quilt` can be used to apply arbitrary patches to embedded sources. The `embed_helpers.cmake` file has the necessary helper functions to download patches, add them to a patch series, and set the patch command to be used with the ExternalProject. These are currently used to patch the embedded libclang build to link to system libraries on Debian. Luckily this is actually pulling patches directly from LLVM's own build repo, which is linked to by https://apt.llvm.org/. You can see the branch associated with the LLVM version. This shows the patch series used to build LLVM 9, etc for Debian. In the case of LLVM 8, to get libclang.a to build only one patch, is needed, not the whole series. The worst possible case in adding support for a new LLVM version is to copy the series file, and set "-p2" or "-p1" depending on if it is LLVM itself or a subproject, and what patch level it was written at. The workflow on a new LLVM release support linking to system libs would be to: * Try it with the existing patch series and maybe it just works * Examine the build failure, and grep for patches affecting these files from those in the patch series on the LLVM 9 / 10 etc branch * Add patches to the series file until there is a minimal one that builds. Most of the time the patches change minor cosmetic things, no major functionality. In this case, the patch makes it from KFreebsd to kFreebsd which breaks a header, but it is easy to fix # Building on CI In order to build successfully in an environment like Travis CI, it is necessary that build jobs complete in less than 50 minutes. Unfortunately, it takes about 50 minutes to build just LLVM dependencies alone on Travis. In order to build a new version of LLVM, or LLVM from a cold cache, the build must be done incrementally. This is done with controlled timeouts, to make the build complete within the 50 minute window. This allows for progress to be saved, and another CI job to pick up where it left off. This is done by setting the `CI_TIMEOUT` environment variable, currently to 40 minutes (to allow time for static bcc to build and cache artifacts to be uploaded). It may take several successive builds to rebuild the LLVM and Clang embedded library dependencies, when a new embedded LLVM target is added or the cache is cleared. Once the cache is warmed, the build times should be comparable to other builds. ## Debug build The debug build is too large to complete successfully on Travis CI. The debug artifacts for LLVM and Clang build process are around 35GB, which simply takes too long to save and restore on travis, resulting in builds being killed due to travis' 10 minute no-output timeout. This makes the incremental approach used to warm the cache for the Release binary impracticable. Debug builds ultimately produce a bpftrace binary that is 1.2GB, and not practical for redistribution. When run locally or on a less restricted CI environment, embedded Debug build should still complete and pass all tests, but it may take 1-2 hours for this to complete from a cold cache. As a way to ensure that debug builds can succeed, the system LLVM may be linked to by setting `EMBED_LLVM=OFF`. # libc options ## glibc This is the default, and currently only tested and supported. bpftrace is built against the glibc from Ubuntu bionic (18.04), which provides 2.27. ## musl (not yet supported) The original static build of bpftrace built against musl statically, but there is no reason why it could not link against musl dynamically. ## bionic (android - not yet supported) It should be possible to link to bionic libc for Android, allowing for bpftrace to run on Android systems with Kernels that support it. ## uclibc (not yet supported) Theoretically no reason this shouldn't work to dynamically link to, may be helpful for supporting bpftrace on embedded environments. bpftrace-0.14.0/docs/fuzzing.md000066400000000000000000000163001413460502400163270ustar00rootroot00000000000000# Fuzzing bpftrace This document is for bpftrace developers. ## Introduction Fuzzing is a method to find bugs in a program automatically. In fuzzing, a fuzzer generates the program input and give it and observes whether the program crashes or not. The most commonly used fuzzing method is called gray box fuzzing, which uses coverage (which parts the program executes) information to generate input efficiently. Fuzzing can be divided into two types according to the target of fuzzing: one that targets the entire program for fuzzing, such as AFL, and the other that targets a specific function, such as libFuzzer. In the former case, a fuzzer generates and supplies the program's input, so you don't need to modify the program. On the other hand, it is not always efficient for large programs, though, in reality, AFL founds a lot of bugs in many programs. The latter is efficient for a function to be fuzzed because a fuzzer directly targets the function, but we need to write some glue code to connect a fuzzer and the function. ## bpftrace options for fuzzing bpftrace has several options useful for fuzzing. ### `BPFTRACE_NODE_MAX` 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_NODE_MAX` environment variable controls the maximum number of AST nodes. ## Fuzzing with AFL Here, I briefly describe the way to fuzz bpftrace with AFL. I highly recommend reading the documentation in the AFL's repository for further information. ### Install AFL (or AFLPlusPlus) Please install [AFL](https://github.com/google/AFL) or [AFLPlusPlus](https://github.com/AFLplusplus/AFLplusplus) according to the instructions. I use AFLPlusPlus because it works well in my environment. AFLPlusPlus is a forked version of AFL (there was a time when the AFL wasn't updated for a while. Now, AFL is hosted on Google's github, and the development is continuing). AFL and AFLPlusPlus have almost the same interface. ### Compile for fuzzing To use AFL, we need to compile the program with the AFL's compiler (it's the wrapper of gcc/clang and do some instrumentation for measuring coverage.) Below is an example of a compile. ``` CC=/path/to/AFLplusplus/afl-clang-fast \ CXX=/path/to/AFLplusplus/afl-clang-fast++ \ AFL_USE_ASAN=1 \ cmake .. \ -DBUILD_FUZZ \ -DFUZZ_TARGET=codegen \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTING=0 \ -DBUILD_ASAN=1 ``` then, ``` AFL_USE_ASAN=1 make -j$(nproc) ``` Important points: - `-DBUILD_FUZZ` option is required to build bpftrace for fuzzing. It adds `-DFUZZ` to compile options. - `-DFUZZ_TARGET` is used to let the program stop right after the specified process. The supported value is either "semantic" or "codegen". For example, if `-DFUZZ_TARGET=semantic`, then the program stops after a semantic analysis. - AddressSanitizer might take a lot of memory. If you want to fuzz without it, please remove `AFL_USE_ASAN` and `-DBUILD_ASAN`. ### Let's Fuzzing First, AFL requires some settings for efficient fuzzing. ``` echo core | sudo tee -a /proc/sys/kernel/core_pattern cd /sys/devices/system/cpu echo performance | sudo tee cpu*/cpufreq/scaling_governor ``` Then, start fuzzing! AFL and AddressSanitizer have a lot of settings, so please read each documentation for the details. The sample way to run fuzzer is like below: ``` CPU=0 FUZZER=/path/to/AFLplusplus/afl-fuzz sudo AFL_NO_AFFINITY=1 \ ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:symbolize=0 \ BPFTRACE_NODE_MAX=200 \ taskset -c ${CPU} \ $FUZZER -M 0 -m none -i ./input -o ./output -t 3000 -- \ ./src/bpftrace_fuzz @@ ``` I describe several important things: - `bpftrace_fuzz` is a fuzzer that we built. It's a slightly modified version of bpftrace for fuzzing, and the first argument is the script file name. If the argument is not given, it reads a script from stdin. - `-i` is the input directory, and `-o` is the output directory. In the input directory, you need to put something to start fuzzing. The most simple example is `echo a > input/a`. More sophisticated inputs can be created by extracting the bpftrace program from the source code directory (especially from tests directory. See the "Input corpus creation" section below for the details.) If some inputs that cause a program crash is found, `output/crashes` contains them. - bpftrace has known that it has several memory leaks. Therefore `ASAN_OPTIONS=detect_leaks=0` is needed. Otherwise, fuzzer thinks each memory leak as a crash and report it. Also, `abort_on_error=1: symbolize=0` is required for fuzzing. - `-t 3000` is the timeout value of each execution. Because (especially codegen) sometimes take a long time to process, it is important to have a longer timeout. Otherwise, AFL would stop fuzzing. - `@@` will be replaced by the input file generated by the fuzzer. ## Fuzzing with libFuzzer [LibFuzzer](https://llvm.org/docs/LibFuzzer.html) is a coverage-guided fuzzer developed with llvm/clang, and bpftrace can be fuzzed with it. ### Compile with libFuzzer To use the libFuzzer, use clang and compile it as follows. ``` CC=clang-10 CXX=clang++-10 cmake .. -DBUILD_ASAN=1 -DBUILD_FUZZ=1 -DUSE_LIBFUZZER=1 -DBUILD_TESTING=0 ``` `-DBUILD_FUZZ=1` and `-DUSE_LIBFUZZER=1` is necessary. ### Fuzzing with libFuzzer The compiled binary itself is a fuzzer, and fuzzing can be performed with commands like the following. ``` sudo ASAN_OPTIONS=detect_leaks=0 ./src/bpftrace_fuzz -max_len=1024 input ``` "input" is a corpus of inputs, just as it is used in AFL. Unlike the AFL, the libFuzzer stops when it crashes. ## Input corpus creation Here are some examples of creating input corpus. - Extract scripts from runtime tests (only the ones enclosed in "") ```sh cat ./tests/runtime/scripts/* | grep RUN | grep -- "-e"| \ sed "s/[^']*'\([^']*\)'.*/\1/" | parallel -N1 "echo {} > input/{#}"` ``` - Use bpftrace tools but remove comments and blank lines ```sh find ./tools -name "*.bt" | \ parallel -N1 "sed -e '/^#\!/d' -e '/\/\*.*/d' -e '/^\s\*.*/d' -e '/\/\/.*/d' -e 's/^\s\+//g' -e '/^$/d' {} > input/{#}" ``` ## Found bugs ### AFL - [#1623](https://github.com/iovisor/bpftrace/pull/1623) - [#1619](https://github.com/iovisor/bpftrace/pull/1619) - [#1580](https://github.com/iovisor/bpftrace/pull/1580) - [#1573](https://github.com/iovisor/bpftrace/pull/1573) - [#1572](https://github.com/iovisor/bpftrace/pull/1572) - [#1570](https://github.com/iovisor/bpftrace/pull/1570) - [#1568](https://github.com/iovisor/bpftrace/pull/1568) - [#1286](https://github.com/iovisor/bpftrace/pull/1286) - [#1245](https://github.com/iovisor/bpftrace/pull/1245) - [#1234](https://github.com/iovisor/bpftrace/pull/1234) - [#1229](https://github.com/iovisor/bpftrace/pull/1229) - [#1224](https://github.com/iovisor/bpftrace/pull/1224) - [#1222](https://github.com/iovisor/bpftrace/pull/1222) - [#1221](https://github.com/iovisor/bpftrace/pull/1221) - [#1210](https://github.com/iovisor/bpftrace/pull/1210) - [#1205](https://github.com/iovisor/bpftrace/pull/1205) ### libFuzzer - [#1653](https://github.com/iovisor/bpftrace/pull/1653) - [#1650](https://github.com/iovisor/bpftrace/pull/1650) - [#1622](https://github.com/iovisor/bpftrace/pull/1622) - [#1621](https://github.com/iovisor/bpftrace/pull/1621) bpftrace-0.14.0/docs/internals_development.md000066400000000000000000000674631413460502400212540ustar00rootroot00000000000000# bpftrace Internals This document is for bpftrace internals developers.
# 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. ## 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/iovisor/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/iovisor/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/iovisor/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/iovisor/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/iovisor/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/iovisor/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/iovisor/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.14.0/docs/reference_guide.md000066400000000000000000003145261413460502400177610ustar00rootroot00000000000000# bpftrace Reference Guide For a reference summary, see the [README.md](../README.md) for the sections on [Probe types](../README.md#probe-types) as well as the [Probes](#probes), [Variable builtins](#1-builtins), and [Function builtins](#1-builtins-1) sections in this guide. This is a work in progress. If something is missing, check the bpftrace source to see if these docs are just out of date. And if you find something, please file an issue or pull request to update these docs. Also, please keep these docs as terse as possible to maintain it's brevity (inspired by the 6-page awk summary from page 106 of [v7vol2b.pdf](https://9p.io/7thEdMan/bswv7.html)). Leave longer examples and discussion to other files in /docs, the /tools/\*\_examples.txt files, or blog posts and other articles. ## Contents - [Terminology](#terminology) - [Usage](#usage) - [1. Hello World](#1-hello-world) - [2. `-e 'program'`: One-Liners](#2--e-program-one-liners) - [3. `filename`: Program Files](#3-filename-program-files) - [4. `-l`: Listing Probes](#4--l-listing-probes) - [5. `-d`: Debug Output](#5--d-debug-output) - [6. `-v`: Verbose Output](#6--v-verbose-output) - [7. Preprocessor Options](#7-preprocessor-options) - [8. Other Options](#8-other-options) - [9. Environment Variables](#9-environment-variables) - [10. Clang Environment Variables](#10-clang-environment-variables) - [Language](#language) - [1. `{...}`: Action Blocks](#1--action-blocks) - [2. `/.../`: Filtering](#2--filtering) - [3. `//`, `/*`: Comments](#3---comments) - [4. Literals](#4-literals) - [5. `->`: C Struct Navigation](#5---c-struct-navigation) - [6. `struct`: Struct Declaration](#6-struct-struct-declaration) - [7. `? :`: ternary operators](#7---ternary-operators) - [8. `if () {...} else {...}`: if-else statements](#8-if---else--if-else-statements) - [9. `unroll () {...}`: unroll](#9-unroll---unroll) - [10. `++ and --`: increment operators](#10--and----increment-operators) - [11. `[]`: Array access](#11--array-access) - [12. Integer casts](#12-integer-casts) - [13. Looping constructs](#13-looping-constructs) - [14. `return`: Terminate Early](#14-return-terminate-early) - [15. `( , )`: Tuples](#15----tuples) - [Probes](#probes) - [1. `kprobe`/`kretprobe`: Dynamic Tracing, Kernel-Level](#1-kprobekretprobe-dynamic-tracing-kernel-level) - [2. `kprobe`/`kretprobe`: Dynamic Tracing, Kernel-Level Arguments](#2-kprobekretprobe-dynamic-tracing-kernel-level-arguments) - [3. `uprobe`/`uretprobe`: Dynamic Tracing, User-Level](#3-uprobeuretprobe-dynamic-tracing-user-level) - [4. `uprobe`/`uretprobe`: Dynamic Tracing, User-Level Arguments](#4-uprobeuretprobe-dynamic-tracing-user-level-arguments) - [5. `tracepoint`: Static Tracing, Kernel-Level](#5-tracepoint-static-tracing-kernel-level) - [6. `tracepoint`: Static Tracing, Kernel-Level Arguments](#6-tracepoint-static-tracing-kernel-level-arguments) - [7. `usdt`: Static Tracing, User-Level](#7-usdt-static-tracing-user-level) - [8. `usdt`: Static Tracing, User-Level Arguments](#8-usdt-static-tracing-user-level-arguments) - [9. `profile`: Timed Sampling Events](#9-profile-timed-sampling-events) - [10. `interval`: Timed Output](#10-interval-timed-output) - [11. `software`: Pre-defined Software Events](#11-software-pre-defined-software-events) - [12. `hardware`: Pre-defined Hardware Events](#12-hardware-pre-defined-hardware-events) - [13. `BEGIN`/`END`: Built-in events](#13-beginend-built-in-events) - [14. `watchpoint`/`asyncwatchpoint`: Memory watchpoints](#14-watchpointasyncwatchpoint-memory-watchpoints) - [15. `kfunc`/`kretfunc`: Kernel Functions Tracing](#15-kfunckretfunc-kernel-functions-tracing) - [16. `kfunc`/`kretfunc`: Kernel Functions Tracing Arguments](#16-kfunckretfunc-kernel-functions-tracing-arguments) - [17. `iter`: Iterators Tracing ](#17-iter-iterators-tracing) - [Variables](#variables) - [1. Builtins](#1-builtins) - [2. `@`, `$`: Basic Variables](#2---basic-variables) - [3. `@[]`: Associative Arrays](#3--associative-arrays) - [4. `count()`: Frequency Counting](#4-count-frequency-counting) - [5. `hist()`, `lhist()`: Histograms](#5-hist-lhist-histograms) - [6. `nsecs`: Timestamps and Time Deltas](#6-nsecs-timestamps-and-time-deltas) - [7. `kstack`: Stack Traces, Kernel](#7-kstack-stack-traces-kernel) - [8. `ustack`: Stack Traces, User](#8-ustack-stack-traces-user) - [9. `$1`, ..., `$N`, `$#`: Positional Parameters](#9-1--n--positional-parameters) - [Functions](#functions) - [1. Builtins](#1-builtins-1) - [2. `printf()`: Print Formatted](#2-printf-Printing) - [3. `time()`: Time](#3-time-time) - [4. `join()`: Join](#4-join-join) - [5. `str()`: Strings](#5-str-strings) - [6. `ksym()`: Symbol Resolution, Kernel-Level](#6-ksym-symbol-resolution-kernel-level) - [7. `usym()`: Symbol Resolution, User-Level](#7-usym-symbol-resolution-user-level) - [8. `kaddr()`: Address Resolution, Kernel-Level](#8-kaddr-address-resolution-kernel-level) - [9. `uaddr()`: Address Resolution, User-Level](#9-uaddr-address-resolution-user-level) - [10. `reg()`: Registers](#10-reg-registers) - [11. `system()`: System](#11-system-system) - [12. `exit()`: Exit](#12-exit-exit) - [13. `cgroupid()`: Resolve cgroup ID](#13-cgroupid-resolve-cgroup-id) - [14. `ntop()`: Convert IP address data to text](#14-ntop-convert-ip-address-data-to-text) - [15. `kstack()`: Stack Traces, Kernel](#15-kstack-stack-traces-kernel) - [16. `ustack()`: Stack Traces, User](#16-ustack-stack-traces-user) - [17. `cat()`: Print file content](#17-cat-print-file-content) - [18. `signal()`: Send a signal to the current task](#18-signal-send-a-signal-to-current-task) - [19. `strncmp()`: Compare first n characters of two strings](#19-strncmp-compare-first-n-characters-of-two-strings) - [20. `override()`: Override return value](#20-override-override-return-value) - [21. `buf()`: Buffers](#21-buf-buffers) - [22. `sizeof()`: Size of type or expression](#22-sizeof-size-of-type-or-expression) - [23. `print()`: Print Value](#23-print-print-value) - [24. `strftime()`: Formatted timestamp](#24-strftime-formatted-timestamp) - [25. `path()`: Return full path](#25-path-return-full-path) - [26. `uptr()`: Annotate userspace pointer](#26-uptr-annotate-userspace-pointer) - [27. `kptr()`: Annotate kernelspace pointer](#27-kptr-annotate-kernelspace-pointer) - [28. `macaddr()`: Convert MAC address data to text](#28-macaddr-convert-mac-address-data-to-text) - [Map Functions](#map-functions) - [1. Builtins](#1-builtins-2) - [2. `count()`: Count](#2-count-count) - [3. `sum()`: Sum](#3-sum-sum) - [4. `avg()`: Average](#4-avg-average) - [5. `min()`: Minimum](#5-min-minimum) - [6. `max()`: Maximum](#6-max-maximum) - [7. `stats()`: Stats](#7-stats-stats) - [8. `hist()`: Log2 Histogram](#8-hist-log2-histogram) - [9. `lhist()`: Linear Histogram](#9-lhist-linear-histogram) - [10. `print()`: Print Map](#10-print-print-map) - [Output](#output) - [1. `printf()`: Per-Event Output](#1-printf-per-event-output) - [2. `interval`: Interval Output](#2-interval-interval-output) - [3. `hist()`, `printf()`: Histogram Printing](#3-hist-print-histogram-printing) - [BTF Support](#btf-support) - [Advanced Tools](#advanced-tools) - [Errors](#errors) # Terminology Term | Description ---- | ----------- BPF | Berkeley Packet Filter: a kernel technology originally developed for optimizing the processing of packet filters (eg, tcpdump expressions) 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. 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. 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. tracepoints | A Linux kernel technology for providing static tracing. kprobes | A Linux kernel technology for providing dynamic tracing of kernel functions. 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. 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. # Usage Command line usage is summarized by bpftrace without options: ``` # bpftrace USAGE: bpftrace [options] filename bpftrace [options] -e 'program' OPTIONS: -B MODE output buffering mode ('line', 'full', or 'none') -d debug info dry run -dd verbose debug info dry run -e 'program' execute this program -h show this help message -I DIR add the specified DIR to the search path for include files. --include FILE adds an implicit #include which is read before the source file is preprocessed. -l [search] list probes -p PID enable USDT probes on PID -c 'CMD' run CMD and enable USDT probes on resulting process -q keep messages quiet -v verbose messages -k emit a warning when a bpf helper returns an error (except read functions) -kk check all bpf helper functions --version bpftrace version ENVIRONMENT: BPFTRACE_STRLEN [default: 64] bytes on BPF stack per str() BPFTRACE_NO_CPP_DEMANGLE [default: 0] disable C++ symbol demangling BPFTRACE_MAP_KEYS_MAX [default: 4096] max keys in a map BPFTRACE_MAX_PROBES [default: 512] max number of probes bpftrace can attach to BPFTRACE_CACHE_USER_SYMBOLS [default: auto] enable user symbol cache BPFTRACE_VMLINUX [default: none] vmlinux path used for kernel symbol resolution BPFTRACE_BTF [default: none] BTF file EXAMPLES: bpftrace -l '*sleep*' list probes containing "sleep" bpftrace -e 'kprobe:do_nanosleep { printf("PID %d sleeping...\n", pid); }' trace processes calling sleep bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' count syscalls by process name ``` ## 1. Hello World The most basic example of a bpftrace program: ``` # bpftrace -e 'BEGIN { printf("Hello, World!\n"); }' Attaching 1 probe... Hello, World! ^C ``` The syntax to this program will be explained in the [Language](#language) section. In this section, we'll cover tool usage. 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: this behavior, and maps, are explained in later sections. ## 2. `-e 'program'`: One-Liners The `-e` option allows a program to be specified, and is a way to construct one-liners: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s is sleeping.\n", comm); }' Attaching 1 probe... iscsid is sleeping. irqbalance is sleeping. iscsid is sleeping. iscsid is sleeping. [...] ``` This example is printing when processes call the nanosleep syscall. Again, the syntax of the program will be explained in the [Language](#language) section. ## 3. `filename`: Program Files Programs saved as files are often called scripts, and can be executed by specifying their file name. We'll often use a `.bt` file extension, short for bpftrace, but the extension is ignored. For example, listing the sleepers.bt file using `cat -n` (which enumerates the output lines): ``` # cat -n sleepers.bt 1 tracepoint:syscalls:sys_enter_nanosleep 2 { 3 printf("%s is sleeping.\n", comm); 4 } ``` Running sleepers.bt: ``` # bpftrace sleepers.bt Attaching 1 probe... iscsid is sleeping. iscsid is sleeping. [...] ``` It can also be made executable to run stand-alone. Start by adding an interpreter line at the top (`#!`) with either the path to your installed bpftrace (/usr/local/bin is the default) or the path to `env` (usually just `/usr/bin/env`) followed by `bpftrace` (so it will find bpftrace in your `$PATH`): ``` 1 #!/usr/local/bin/bpftrace 2 3 tracepoint:syscalls:sys_enter_nanosleep 4 { 5 printf("%s is sleeping.\n", comm); 6 } ``` Then make it executable: ``` # chmod 755 sleepers.bt # ./sleepers.bt Attaching 1 probe... iscsid is sleeping. iscsid is sleeping. [...] ``` ## 4. `-l`: Listing Probes Probes from the tracepoint and kprobe libraries can be listed with `-l`. ``` # bpftrace -l | more tracepoint:xfs:xfs_attr_list_sf tracepoint:xfs:xfs_attr_list_sf_all tracepoint:xfs:xfs_attr_list_leaf tracepoint:xfs:xfs_attr_list_leaf_end [...] # bpftrace -l | wc -l 46260 ``` Other libraries generate probes dynamically, such as uprobe, and require specific ways to determine available probes. See the later [Probes](#probes) sections. Search terms can be added: ``` # bpftrace -l '*nanosleep*' tracepoint:syscalls:sys_enter_clock_nanosleep tracepoint:syscalls:sys_exit_clock_nanosleep tracepoint:syscalls:sys_enter_nanosleep tracepoint:syscalls:sys_exit_nanosleep kprobe:nanosleep_copyout kprobe:hrtimer_nanosleep [...] ``` The `-v` option when listing tracepoints will show their arguments for use from the args builtin. For example: ``` # bpftrace -lv tracepoint:syscalls:sys_enter_open tracepoint:syscalls:sys_enter_open int __syscall_nr; const char * filename; int flags; umode_t mode; ``` If BTF is available, it is also possible to list struct/union/enum definitions. For example: ``` # bpftrace -lv "struct path" struct path { struct vfsmount *mnt; struct dentry *dentry; }; ``` ## 5. `-d`: Debug Output The `-d` option produces debug output, and does not run the program. This is mostly useful for debugging issues with bpftrace itself. You can also use `-dd` to produce a more verbose debug output, which will also print unoptimized IR. **If you are an end-user of bpftrace, you should not normally need the `-d` or `-v` options, and you can skip to the [Language](#language) section.** ``` # bpftrace -d -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s is sleeping.\n", comm); }' Program tracepoint:syscalls:sys_enter_nanosleep call: printf string: %s is sleeping.\n builtin: comm [...] ``` The output begins with `Program` and then an abstract syntax tree (AST) representation of the program. Continued: ``` [...] %printf_t = type { i64, [16 x i8] } [...] define i64 @"tracepoint:syscalls:sys_enter_nanosleep"(i8*) local_unnamed_addr section "s_tracepoint:syscalls:sys_enter_nanosleep" { entry: %comm = alloca [16 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) %2 = getelementptr inbounds [16 x i8], [16 x i8]* %comm, i64 0, i64 0 %3 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* nonnull %3, i8 0, i64 24, i32 8, i1 false) call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2) call void @llvm.memset.p0i8.i64(i8* nonnull %2, i8 0, i64 16, i32 1, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 (i8*, i64)*)([16 x i8]* nonnull %comm, i64 16) %4 = getelementptr inbounds %printf_t, %printf_t* %printf_args, i64 0, i32 1, i64 0 call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull %4, i8* nonnull %2, i64 16, 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 24) call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1) ret i64 0 [...] ``` This section shows the llvm intermediate representation (IR) assembly, which is then compiled into BPF. ## 6. `-v`: 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); }' Attaching 1 probe... The verifier log: 0: (bf) r6 = r1 1: (b7) r1 = 0 2: (7b) *(u64 *)(r10 -24) = r1 3: (7b) *(u64 *)(r10 -32) = r1 4: (7b) *(u64 *)(r10 -40) = r1 5: (7b) *(u64 *)(r10 -8) = r1 6: (7b) *(u64 *)(r10 -16) = r1 7: (bf) r1 = r10 8: (07) r1 += -16 9: (b7) r2 = 16 10: (85) call bpf_get_current_comm#16 11: (79) r1 = *(u64 *)(r10 -16) 12: (7b) *(u64 *)(r10 -32) = r1 13: (79) r1 = *(u64 *)(r10 -8) 14: (7b) *(u64 *)(r10 -24) = r1 15: (18) r7 = 0xffff9044e65f1000 17: (85) call bpf_get_smp_processor_id#8 18: (bf) r4 = r10 19: (07) r4 += -40 20: (bf) r1 = r6 21: (bf) r2 = r7 22: (bf) r3 = r0 23: (b7) r5 = 24 24: (85) call bpf_perf_event_output#25 25: (b7) r0 = 0 26: (95) exit processed 26 insns (limit 131072), stack depth 40 Attaching tracepoint:syscalls:sys_enter_nanosleep iscsid is sleeping. iscsid is sleeping. [...] ``` This includes `The verifier log:` and then the log message from the in-kernel verifier. ## 7. Preprocessor Options The `-I` option can be used to add directories to the list of directories that bpftrace uses to look for headers. Can be defined multiple times. ``` # cat program.bt #include BEGIN { @ = FOO } # bpftrace program.bt definitions.h:1:10: fatal error: 'foo.h' file not found # /tmp/include foo.h # bpftrace -I /tmp/include program.bt Attaching 1 probe... ``` The `--include` option can be used to include headers by default. Can be defined multiple times. Headers are included in the order they are defined, and they are included before any other include in the program being executed. ``` # bpftrace --include linux/path.h --include linux/dcache.h \ -e 'kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); }' Attaching 1 probe... open path: .com.google.Chrome.ASsbu2 open path: .com.google.Chrome.gimc10 open path: .com.google.Chrome.R1234s ``` ## 8. Other Options - The `--version` option prints the bpftrace version: ``` # bpftrace --version bpftrace v0.8-90-g585e-dirty ``` - The `--no-warnings` option disables warnings. ## 9. Environment Variables ### 9.1 `BPFTRACE_STRLEN` Default: 64 Number of bytes allocated on the BPF stack for the string returned by str(). Make this larger if you wish to read bigger strings with str(). Beware that the BPF stack is small (512 bytes), and that you pay the toll again inside printf() (whilst it composes a perf event output buffer). So in practice you can only grow this to about 200 bytes. Support for even larger strings is [being discussed](https://github.com/iovisor/bpftrace/issues/305). ### 9.2 `BPFTRACE_NO_CPP_DEMANGLE` Default: 0 C++ symbol demangling in userspace stack traces is enabled by default. This feature can be turned off by setting the value of this environment variable to `1`. ### 9.3 `BPFTRACE_MAP_KEYS_MAX` 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. ### 9.4 `BPFTRACE_MAX_PROBES` Default: 512 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 or crash the system. ### 9.5 `BPFTRACE_CACHE_USER_SYMBOLS` Default: 0 if ASLR is enabled on system and `-c` option is not given; otherwise 1 By default, bpftrace caches the results of symbols resolutions only when ASLR (Address Space Layout Randomization) is disabled. This is because the symbol addresses change with each execution with ASLR. However, disabling caching may incur some performance. Set this env variable to 1 to force bpftrace to cache. This is fine if only trace one program execution. ### 9.6 `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. ### 9.7 `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. ### 9.8 `BPFTRACE_PERF_RB_PAGES` Default: 64 Number of pages to allocate per CPU for 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. ## 10. 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. # Language ## 1. `{...}`: Action Blocks Syntax: `probe[,probe,...] /filter/ { action }` A bpftrace program can have multiple action blocks. The filter is optional. Example: ``` # bpftrace -e 'kprobe:do_sys_open { printf("opening: %s\n", str(arg1)); }' Attaching 1 probe... opening: /proc/cpuinfo opening: /proc/stat opening: /proc/diskstats opening: /proc/stat opening: /proc/vmstat [...] ``` This is a one-liner invocation of bpftrace. The probe is `kprobe:do_sys_open`. When that probe "fires" (the instrumentation event occurred) the action will be executed, which consists of a `print()` statement. Explanations of the probe and action are in the sections that follow. ## 2. `/.../`: Filtering Syntax: `/filter/` 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. Examples: ``` # bpftrace -e 'kprobe:vfs_read /arg2 < 16/ { printf("small read: %d byte buffer\n", arg2); }' Attaching 1 probe... small read: 8 byte buffer small read: 8 byte buffer small read: 8 byte buffer small read: 8 byte buffer small read: 8 byte buffer small read: 12 byte buffer ^C ``` ``` # bpftrace -e 'kprobe:vfs_read /comm == "bash"/ { printf("read by %s\n", comm); }' Attaching 1 probe... read by bash read by bash read by bash read by bash ^C ``` ## 3. `//`, `/*`: Comments Syntax ``` // single-line comment /* * multi-line comment */ ``` These can be used in bpftrace scripts to document your code. ## 4. Literals Integer, char and string literals are supported. Integer literals are a sequence of digits with an optional underscore (`_`) as field separator. Scientific notation is also supported but only for integer values as BPF doesn't support floating point. ``` # bpftrace -e 'BEGIN { printf("%lu %lu %lu", 1000000, 1e6, 1_000_000)}' Attaching 1 probe... 1000000 1000000 1000000 ``` Char literals are enclosed in single quotes, e.g. `'a'` and `'@'`. String literals are enclosed in double quotes, e.g. `"a string"`. ## 5. `->`: C Struct Navigation tracepoint example: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }' Attaching 1 probe... snmpd /proc/diskstats snmpd /proc/stat snmpd /proc/vmstat [...] ``` This is returning the `filename` member from the `args` struct, which for tracepoint probes contains the tracepoint arguments. See the [Static Tracing, Kernel-Level Arguments](#6-tracepoint-static-tracing-kernel-level-arguments) section for the contents of this struct. kprobe example: ``` # cat path.bt #include #include kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` This uses dynamic tracing of the `vfs_open()` kernel function, via the short script path.bt. Some kernel headers needed to be included to understand the `path` and `dentry` structs. ## 6. `struct`: Struct Declaration Example: ``` // from fs/namei.c: struct nameidata { struct path path; struct qstr last; // [...] }; ``` You can define your own structs when needed. In some cases, kernel structs are not declared in the kernel headers package, and are declared manually in bpftrace tools (or partial structs are: enough to reach the member to dereference). ## 7. `? :`: ternary operators Examples: ``` # bpftrace -e 'tracepoint:syscalls:sys_exit_read { @error[args->ret < 0 ? - args->ret : 0] = count(); }' Attaching 1 probe... ^C @error[11]: 24 @error[0]: 78 ``` ``` # bpftrace -e 'BEGIN { pid & 1 ? printf("Odd\n") : printf("Even\n"); exit(); }' Attaching 1 probe... Odd ``` ## 8. `if () {...} else {...}`: if-else statements Example: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_read { @reads = count(); if (args->count > 1024) { @large = count(); } }' Attaching 1 probe... ^C @large: 72 @reads: 80 ``` ## 9. `unroll () {...}`: Unroll Example: ``` # bpftrace -e 'kprobe:do_nanosleep { $i = 1; unroll(5) { printf("i: %d\n", $i); $i = $i + 1; } }' Attaching 1 probe... i: 1 i: 2 i: 3 i: 4 i: 5 ^C ``` ## 10. `++` and `--`: Increment operators `++` and `--` can be used to conveniently increment or decrement counters in maps or variables. 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. Example - variable: ``` bpftrace -e 'BEGIN { $x = 0; $x++; $x++; printf("x: %d\n", $x); }' Attaching 1 probe... x: 2 ^C ``` Example - map: ``` bpftrace -e 'k:vfs_read { @++ }' Attaching 1 probe... ^C @: 12807 ``` Example - map with key: ``` # bpftrace -e 'k:vfs_read { @[probe]++ }' Attaching 1 probe... ^C @[kprobe:vfs_read]: 13369 ``` ## 11. `[]`: Array Access You may access one-dimensional constant arrays with the array access operator `[]`. Example: ``` # bpftrace -e 'struct MyStruct { int y[4]; } uprobe:./testprogs/array_access:test_struct { $s = (struct MyStruct *) arg0; @x = $s->y[0]; exit(); }' Attaching 1 probe... @x: 1 ``` ## 12. Integer casts Integers are internally represented as 64 bit signed. If you need another representation, you may cast to the following built in types: | Type | Explanation | |----------|--------------------------| | `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 | Example: ``` # bpftrace -e 'BEGIN { $x = 1<<16; printf("%d %d\n", (uint16)$x, $x); }' Attaching 1 probe... 0 65536 ^C ``` ## 13. Looping Constructs **Experimental** Kernel: 5.3 bpftrace supports C style while loops: ``` # bpftrace -e 'i:ms:100 { $i = 0; while ($i <= 100) { printf("%d ", $i); $i++} exit(); }' ``` Loops can be short circuited by using the `continue` and `break` keywords. ## 14. `return`: Terminate Early The `return` keyword is used to exit the current probe. This differs from `exit()` in that it doesn't exit bpftrace. ## 15. `( , )`: Tuples N-tuples are supported, where N is any integer greater than 1. Indexing is supported using the `.` operator. Tuples are immutable once created. Example: ``` # bpftrace -e 'BEGIN { $t = (1, 2, "string"); printf("%d %s\n", $t.1, $t.2); }' Attaching 1 probe... 2 string ^C ``` # Probes - `kprobe` - kernel function start - `kretprobe` - kernel function return - `uprobe` - user-level function start - `uretprobe` - user-level function return - `tracepoint` - kernel static tracepoints - `usdt` - user-level static tracepoints - `profile` - timed sampling - `interval` - timed output - `software` - kernel software events - `hardware` - processor-level events Some probe types allow wildcards to match multiple probes, eg, `kprobe:vfs_*`. You may also specify multiple attach points for an action block using a comma separated list. Quoted strings (eg. `uprobe:"/usr/lib/c++lib.so":foo`) may be used to escape characters in attach point definitions. ## 1. `kprobe`/`kretprobe`: Dynamic Tracing, Kernel-Level Syntax: ``` kprobe:function_name[+offset] kretprobe:function_name ``` These use kprobes (a Linux kernel capability). `kprobe` instruments the beginning of a function's execution, and `kretprobe` instruments the end (its return). Examples: ``` # bpftrace -e 'kprobe:do_nanosleep { printf("sleep by %d\n", tid); }' Attaching 1 probe... sleep by 1396 sleep by 3669 sleep by 1396 sleep by 27662 sleep by 3669 ^C ``` It's also possible to specify offset within the probed function: ``` # gdb -q /usr/lib/debug/boot/vmlinux-`uname -r` --ex 'disassemble do_sys_open' Reading symbols from /usr/lib/debug/boot/vmlinux-5.0.0-32-generic...done. Dump of assembler code for function do_sys_open: 0xffffffff812b2ed0 <+0>: callq 0xffffffff81c01820 <__fentry__> 0xffffffff812b2ed5 <+5>: push %rbp 0xffffffff812b2ed6 <+6>: mov %rsp,%rbp 0xffffffff812b2ed9 <+9>: push %r15 ... # bpftrace -e 'kprobe:do_sys_open+9 { printf("in here\n"); }' Attaching 1 probe... in here ... ``` The address is being checked using vmlinux (with debug symbols) if it's aligned with instruction boundaries and within the function. If it's not, we fail to add it: ``` # bpftrace -e 'kprobe:do_sys_open+1 { printf("in here\n"); }' Attaching 1 probe... Could not add kprobe into middle of instruction: /usr/lib/debug/boot/vmlinux-5.0.0-32-generic:do_sys_open+1 ``` If bpftrace is compiled with `ALLOW_UNSAFE_PROBE` option, you can use --unsafe option to skip the check. In this case, linux kernel still checks instruction alignment. The default vmlinux path can be overridden using the environment variable `BPFTRACE_VMLINUX`. Examples in situ: [(kprobe) search /tools](https://github.com/iovisor/bpftrace/search?q=kprobe%3A+path%3Atools&type=Code) [(kretprobe) /tools](https://github.com/iovisor/bpftrace/search?q=kretprobe%3A+path%3Atools&type=Code) ## 2. `kprobe`/`kretprobe`: Dynamic Tracing, Kernel-Level Arguments Syntax: ``` kprobe: arg0, arg1, ..., argN kretprobe: retval ``` Arguments can be accessed via these variables names. `arg0` is the first argument and can only be accessed with a `kprobe`. `retval` is the return value for the instrumented function, and can only be accessed on `kretprobe`. Examples: ``` # bpftrace -e 'kprobe:do_sys_open { printf("opening: %s\n", str(arg1)); }' Attaching 1 probe... opening: /proc/cpuinfo opening: /proc/stat opening: /proc/diskstats opening: /proc/stat opening: /proc/vmstat [...] ``` ``` # bpftrace -e 'kprobe:do_sys_open { printf("open flags: %d\n", arg2); }' Attaching 1 probe... open flags: 557056 open flags: 32768 open flags: 32768 open flags: 32768 [...] ``` ``` # bpftrace -e 'kretprobe:do_sys_open { printf("returned: %d\n", retval); }' Attaching 1 probe... returned: 8 returned: 21 returned: -2 returned: 21 [...] ``` As an example of struct arguments: ``` # cat path.bt #include #include kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` Here arg0 was casted 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 some structs. If the kernel has BTF (BPF Type Format) data, all kernel structs are always available without defining them. For example: ``` # bpftrace -e 'kprobe:vfs_open { printf("open path: %s\n", \ str(((struct path *)arg0)->dentry->d_name.name)); }' Attaching 1 probe... open path: cmdline open path: interrupts [...] ``` See [BTF Support](#btf-support) for more details. Examples in situ: [(kprobe) search /tools](https://github.com/iovisor/bpftrace/search?q=kprobe%3A+path%3Atools&type=Code) [(kretprobe) /tools](https://github.com/iovisor/bpftrace/search?q=kretprobe%3A+path%3Atools&type=Code) ## 3. `uprobe`/`uretprobe`: Dynamic Tracing, User-Level Syntax: ``` uprobe:library_name:function_name[+offset] uprobe:library_name:address uretprobe:library_name:function_name ``` These use uprobes (a Linux kernel capability). `uprobe` instruments the beginning of a user-level function's execution, and `uretprobe` instruments the end (its return). To list available uprobes, you can use any program to list the text segment symbols from a binary, such as `objdump` and `nm`. For example: ``` # objdump -tT /bin/bash | grep readline 00000000007003f8 g DO .bss 0000000000000004 Base rl_readline_state 0000000000499e00 g DF .text 00000000000001c5 Base readline_internal_char 00000000004993d0 g DF .text 0000000000000126 Base readline_internal_setup 000000000046d400 g DF .text 000000000000004b Base posix_readline_initialize 000000000049a520 g DF .text 0000000000000081 Base readline [...] ``` This has listed various functions containing "readline" from /bin/bash. These can be instrumented using `uprobe` and `uretprobe`. Examples: ``` # bpftrace -e 'uretprobe:/bin/bash:readline { printf("read a line\n"); }' Attaching 1 probe... read a line read a line read a line ^C ``` While tracing, this has caught a few executions of the `readline()` function in /bin/bash. This example is continued in the next section. It's also possible to specify uprobe with virtual address, like: ``` # objdump -tT /bin/bash | grep main ... 000000000002ec00 g DF .text 0000000000001868 Base main ... # bpftrace -e 'uprobe:/bin/bash:0x2ec00 { printf("in here\n"); }' Attaching 1 probe... ``` And to specify offset within the probed function: ``` # objdump -d /bin/bash ... 000000000002ec00 : 2ec00: f3 0f 1e fa endbr64 2ec04: 41 57 push %r15 2ec06: 41 56 push %r14 2ec08: 41 55 push %r13 ... # bpftrace -e 'uprobe:/bin/bash:main+4 { printf("in here\n"); }' Attaching 1 probe... ... ``` The address is being checked if it's aligned with instruction boundaries. If it's not, we fail to add it: ``` # bpftrace -e 'uprobe:/bin/bash:main+1 { printf("in here\n"); }' Attaching 1 probe... Could not add uprobe into middle of instruction: /bin/bash:main+1 ``` If bpftrace is compiled with `ALLOW_UNSAFE_PROBE` option, you can use --unsafe option to skip the check: ``` # bpftrace -e 'uprobe:/bin/bash:main+1 { printf("in here\n"); } --unsafe' Attaching 1 probe... Unsafe uprobe in the middle of the instruction: /bin/bash:main+1 ``` Using --unsafe option you can also place uprobes on arbitrary addresses. This might come in handy when the binary is stripped. ``` $ echo 'int main(){return 0;}' | gcc -xc -o bin - $ nm bin | grep main ... 0000000000001119 T main ... $ strip bin # bpftrace --unsafe -e 'uprobe:bin:0x1119 { printf("main called\n"); }' Attaching 1 probe... WARNING: could not determine instruction boundary for uprobe:bin:4377 (binary appears stripped). Misaligned probes can lead to tracee crashes! ``` 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`. ``` # bpftrace -e 'uprobe:libc:malloc { printf("Allocated %d bytes\n", arg0); }' Allocated 4 bytes ... ``` Examples in situ: [(uprobe) search /tools](https://github.com/iovisor/bpftrace/search?q=uprobe%3A+path%3Atools&type=Code) [(uretprobe) /tools](https://github.com/iovisor/bpftrace/search?q=uretprobe%3A+path%3Atools&type=Code) ## 4. `uprobe`/`uretprobe`: Dynamic Tracing, User-Level Arguments Syntax: ``` uprobe: arg0, arg1, ..., argN uretprobe: retval ``` Arguments can be accessed via these variables names. `arg0` is the first argument, and can only be accessed with a `uprobe`. `retval` is the return value for the instrumented function, and can only be accessed on `uretprobe`. Examples: ``` # bpftrace -e 'uprobe:/bin/bash:readline { printf("arg0: %d\n", arg0); }' Attaching 1 probe... arg0: 19755784 arg0: 19755016 arg0: 19755784 ^C ``` What does `arg0` of `readline()` in /bin/bash contain? I don't know. I'd need to look at the bash source code to find out what its arguments were. ``` # bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc-2.23.so:fopen { printf("fopen: %s\n", str(arg0)); }' Attaching 1 probe... fopen: /proc/filesystems fopen: /usr/share/locale/locale.alias fopen: /proc/self/mountinfo ^C ``` In this case, I know that the first argument of libc `fopen()` is the pathname (see the fopen(3) man page), so I've traced it using a uprobe. Adjust the path to libc to match your system (it may not be libc-2.23.so). A `str()` call is necessary to turn the char * pointer to a string, as explained in a later section. ``` # bpftrace -e 'uretprobe:/bin/bash:readline { printf("readline: \"%s\"\n", str(retval)); }' Attaching 1 probe... readline: "echo hi" readline: "ls -l" readline: "date" readline: "uname -r" ^C ``` Back to the bash `readline()` example: after checking the source code, I saw that the return value was the string read. So I can use a `uretprobe` and the `retval` variable to see the read string. *** If the traced binary has DWARF available, it is possible to access `uprobe` arguments by name. Syntax: ``` uprobe: args->NAME ``` The arguments can be accessed by dereferencing `args` and accessing the argument's `NAME`. Currently, only simple types (integer, char, bool, enum) and pointers to them are supported. The list of function's arguments can be retrieved using the verbose list option: ``` # bpftrace -lv 'uprobe:/bin/bash:rl_set_prompt' uprobe:/bin/bash:rl_set_prompt const char* prompt ``` Example (requires debuginfo for `/bin/bash` installed): ``` # bpftrace -e 'uprobe:/bin/bash:rl_set_prompt { printf("prompt: %s\n", str(args->prompt)); }' Attaching 1 probe... prompt: [user@localhost ~]$ ^C ``` Examples in situ: [(uprobe) search /tools](https://github.com/iovisor/bpftrace/search?q=uprobe%3A+path%3Atools&type=Code) [(uretprobe) /tools](https://github.com/iovisor/bpftrace/search?q=uretprobe%3A+path%3Atools&type=Code) ## 5. `tracepoint`: Static Tracing, Kernel-Level Syntax: `tracepoint:name` These use tracepoints (a Linux kernel capability). ``` # bpftrace -e 'tracepoint:block:block_rq_insert { printf("block I/O created by %d\n", tid); }' Attaching 1 probe... block I/O created by 28922 block I/O created by 3949 block I/O created by 883 block I/O created by 28941 block I/O created by 28941 block I/O created by 28941 [...] ``` Examples in situ: [search /tools](https://github.com/iovisor/bpftrace/search?q=tracepoint%3A+path%3Atools&type=Code) ## 6. `tracepoint`: Static Tracing, Kernel-Level Arguments Example: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }' Attaching 1 probe... irqbalance /proc/interrupts irqbalance /proc/stat snmpd /proc/diskstats snmpd /proc/stat snmpd /proc/vmstat snmpd /proc/net/dev [...] ``` The available members for each tracepoint can be listed from their /format file in /sys. For example: ``` # cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_open/format name: sys_enter_openat ID: 608 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:int __syscall_nr; offset:8; size:4; signed:1; field:int dfd; offset:16; size:8; signed:0; field:const char * filename; offset:24; size:8; signed:0; field:int flags; offset:32; size:8; signed:0; field:umode_t mode; offset:40; size:8; signed:0; print fmt: "dfd: 0x%08lx, filename: 0x%08lx, flags: 0x%08lx, mode: 0x%08lx", ((unsigned long)(REC->dfd)), ((unsigned long)(REC->filename)), ((unsigned long)(REC->flags)), ((unsigned long)(REC->mode)) ``` 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. Examples in situ: [search /tools](https://github.com/iovisor/bpftrace/search?q=tracepoint%3A+path%3Atools&type=Code) ## 7. `usdt`: Static Tracing, User-Level Syntax: ``` usdt:binary_path:probe_name usdt:binary_path:[probe_namespace]:probe_name usdt:library_path:probe_name usdt:library_path:[probe_namespace]:probe_name ``` Where `probe_namespace` is optional if `probe_name` is unique within the binary. Examples: ``` # bpftrace -e 'usdt:/root/tick:loop { printf("hi\n"); }' Attaching 1 probe... hi hi hi hi hi ^C ``` The namespace of the probe is deduced automatically. If the binary `/root/tick` contained multiple probes with the name `loop` (e.g. `tick:loop` and `tock:loop`), no probe would be attached. This may be solved by manually specifying the namespace or by using a wildcard: ``` # bpftrace -e 'usdt:/root/tick:loop { printf("hi\n"); }' ERROR: namespace for usdt:/root/tick:loop not specified, matched 2 probes INFO: please specify a unique namespace or use '*' to attach to all matched probes No probes to attach # bpftrace -e 'usdt:/root/tick:tock:loop { printf("hi\n"); }' Attaching 1 probe... hi hi ^C # bpftrace -e 'usdt:/root/tick:*:loop { printf("hi\n"); }' Attaching 2 probes... hi hi hi hi ^C ``` 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 `execve`d from private mount namespaces or bind mounted directories. One workaround is to run bpftrace inside the appropriate namespaces (ie the container). ## 8. `usdt`: Static Tracing, User-Level Arguments Examples: ``` # bpftrace -e 'usdt:/root/tick:loop { printf("%s: %d\n", str(arg0), arg1); }' my string: 1 my string: 2 my string: 3 my string: 4 my string: 5 ^C ``` ``` # bpftrace -e 'usdt:/root/tick:loop /arg1 > 2/ { printf("%s: %d\n", str(arg0), arg1); }' my string: 3 my string: 4 my string: 5 my string: 6 ^C ``` ## 9. `profile`: Timed Sampling Events Syntax: ``` profile:hz:rate profile:s:rate profile:ms:rate profile:us:rate ``` These operating using perf_events (a Linux kernel facility), which is also used by the `perf` command). Examples: ``` # bpftrace -e 'profile:hz:99 { @[tid] = count(); }' Attaching 1 probe... ^C @[32586]: 98 @[0]: 579 ``` ## 10. `interval`: Timed Output Syntax: ``` interval:ms:rate interval:s:rate interval:us:rate interval:hz:rate ``` This fires on one CPU only, and can be used for generating per-interval output. Example: ``` # bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @syscalls = count(); } interval:s:1 { print(@syscalls); clear(@syscalls); }' Attaching 2 probes... @syscalls: 1263 @syscalls: 731 @syscalls: 891 @syscalls: 1195 @syscalls: 1154 @syscalls: 1635 @syscalls: 1208 [...] ``` This prints the rate of syscalls per second. Examples in situ: [search /tools](https://github.com/iovisor/bpftrace/search?q=interval+extension%3Abt+path%3Atools&type=Code) ## 11. `software`: Pre-defined Software Events Syntax: ``` software:event_name:count software:event_name: ``` 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. 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` The count is the trigger for the probe, which will fire once for every count events. If the count is not provided, a default is used. Examples: ``` # bpftrace -e 'software:faults:100 { @[comm] = count(); }' Attaching 1 probe... ^C @[ls]: 1 @[pager]: 2 @[locale]: 2 @[preconv]: 2 @[sh]: 3 @[tbl]: 3 @[bash]: 4 @[groff]: 5 @[grotty]: 7 @[sleep]: 9 @[nroff]: 12 @[troff]: 18 @[man]: 97 ``` This roughly counts who is causing page faults, by sampling the process name for every one in one hundred faults. ## 12. `hardware`: Pre-defined Hardware Events Syntax: ``` hardware:event_name:count hardware:event_name: ``` 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 is the trigger for the probe, which will fire once for every count events. If the count is not provided, a default is used. Examples: ``` bpftrace -e 'hardware:cache-misses:1000000 { @[pid] = count(); }' ``` That would fire once for every 1000000 cache misses. This usually indicates the last level cache (LLC). ## 13. `BEGIN`/`END`: Built-in events Syntax: ``` 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. Examples in situ: [(BEGIN) search /tools](https://github.com/iovisor/bpftrace/search?q=BEGIN+extension%3Abt+path%3Atools&type=Code) [(END) search /tools](https://github.com/iovisor/bpftrace/search?q=END+extension%3Abt+path%3Atools&type=Code) ## 14. `watchpoint`/`asyncwatchpoint`: Memory watchpoints **WARNING**: this feature is experimental and may be subject to interface changes. Memory watchpoints are also architecture dependant Syntax: ``` watchpoint:absolute_address:length:mode watchpoint:function+argN:length:mode ``` 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` (see [uprobe arguments](#4-uprobeuretprobe-dynamic-tracing-user-level-arguments)) 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`d 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. Examples: ``` bpftrace -e 'watchpoint:0x10000000:8:rw { printf("hit!\n"); exit(); }' -c ./testprogs/watchpoint ``` It will output "hit" and exit when the watchpoint process is trying to read or write 0x10000000. ``` # bpftrace -e "watchpoint:0x$(awk '$3 == "jiffies" {print $1}' /proc/kallsyms):8:w {@[kstack] = count();}" Attaching 1 probe... ^C ...... @[ do_timer+12 tick_do_update_jiffies64.part.22+89 tick_sched_do_timer+103 tick_sched_timer+39 __hrtimer_run_queues+256 hrtimer_interrupt+256 smp_apic_timer_interrupt+106 apic_timer_interrupt+15 cpuidle_enter_state+188 cpuidle_enter+41 do_idle+536 cpu_startup_entry+25 start_secondary+355 secondary_startup_64+164 ]: 319 ``` It shows the kernel stacks in which jiffies is updated. ``` # 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 ``` bpftrace will output "hit" and exit when the memory pointed to by `arg1` of `increment` is written. ## 15. `kfunc`/`kretfunc`: Kernel Functions Tracing Syntax: ``` kfunc:function kretfunc:function ``` These are kernel function probes implemented via eBPF trampolines which allows kernel code to call into BPF programs with practically zero overhead. Examples: ``` # bpftrace -e 'kfunc:x86_pmu_stop { printf("pmu %s stop\n", str(args->event->pmu->name)); }' # bpftrace -e 'kretfunc:fget { printf("fd %d name %s\n", args->fd, str(retval->f_path.dentry->d_name.name)); }' ``` You can get list of available functions via list option: ``` # bpftrace -l ... kfunc:ksys_ioperm kfunc:ksys_unshare kfunc:ksys_setsid kfunc:ksys_sync_helper kfunc:ksys_fadvise64_64 kfunc:ksys_readahead kfunc:ksys_mmap_pgoff ... ``` ## 16. `kfunc`/`kretfunc`: Kernel Functions Tracing Arguments Syntax: ``` kfunc:function args->NAME ... kretfunc:function args->NAME ... retval ``` Arguments can be accessed via args being dereferenced to argument's `NAME`. Return value can be referenced by `retval` builtin, see the [1. Builtins](#1-builtins). It's possible to get available argument names for function via verbose list option: ``` # bpftrace -lv ... kfunc:fget unsigned int fd; struct file * retval; ... ``` The `fget` function takes one argument as file descriptor and you can access it via `args->fd` in `kfunc:fget` probe: ``` # bpftrace -e 'kfunc:fget { printf("fd %d\n", args->fd); }' Attaching 1 probe... fd 3 fd 3 ... ``` The return value of `fget` function probe is accessible via `retval`: ``` # bpftrace -e 'kretfunc:fget { printf("fd %d name %s\n", args->fd, str(retval->f_path.dentry->d_name.name)); }' Attaching 1 probe... fd 3 name ld.so.cache fd 3 name libselinux.so.1 fd 3 name libselinux.so.1 ... ``` And as you can see in above example it's also possible to access function arguments on `kretfunc` probes. ## 17. `iter`: Iterators Tracing **WARNING**: this feature is experimental and may be subject to interface changes. Syntax: ``` iter:task[:pin] iter:task_file[:pin] ``` Kernel: 5.4 These are eBPF iterator probes, that allows iteration over kernel objects. Iterator probe can't be mixed with any other probe, not even other iterator. Each iterator probe provides set of fields that could be accessed with ctx pointer. User can display set of available fields for iterator via -lv options as described below. Examples: ``` # bpftrace -e 'iter:task { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }' Attaching 1 probe... systemd:1 kthreadd:2 rcu_gp:3 rcu_par_gp:4 kworker/0:0H:6 mm_percpu_wq:8 ... # bpftrace -e 'iter:task_file { printf("%s:%d %d:%s\n", ctx->task->comm, ctx->task->pid, ctx->fd, path(ctx->file->f_path)); }' Attaching 1 probe... systemd:1 1:/dev/null systemd:1 2:/dev/null systemd:1 3:/dev/kmsg ... su:1622 1:/dev/pts/1 su:1622 2:/dev/pts/1 su:1622 3:/var/lib/sss/mc/passwd ... bpftrace:1892 1:pipe:[35124] bpftrace:1892 2:/dev/pts/1 bpftrace:1892 3:anon_inode:bpf-map bpftrace:1892 4:anon_inode:bpf-map bpftrace:1892 5:anon_inode:bpf_link bpftrace:1892 6:anon_inode:bpf-prog bpftrace:1892 7:anon_inode:bpf_iter ``` You can get list of available functions via list option: ``` # bpftrace -l iter:* iter:task iter:task_file # bpftrace -l iter:* -v iter:task struct task_struct *task; iter:task_file struct task_struct *task; int fd; struct file *file; ``` It's possible to pin iterator with specifying optional probe ':pin' part, that defines the pin file. It can be specified as absolute path or relative to /sys/fs/bpf. Examples with relative pin file: ``` # bpftrace -e 'iter:task:list { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }' Attaching 1 probe... Program pinned to /sys/fs/bpf/list # cat /sys/fs/bpf/list systemd:1 kthreadd:2 rcu_gp:3 rcu_par_gp:4 kworker/0:0H:6 mm_percpu_wq:8 rcu_tasks_kthre:9 ... ``` Examples with absolute pin file: ``` # bpftrace -e 'iter:task_file:/sys/fs/bpf/files { printf("%s:%d %s\n", ctx->task->comm, ctx->task->pid, path(ctx->file->f_path)); }' Attaching 1 probe... Program pinned to /sys/fs/bpf/files # cat /sys/fs/bpf/files systemd:1 anon_inode:inotify systemd:1 anon_inode:[timerfd] ... systemd-journal:849 /dev/kmsg systemd-journal:849 anon_inode:[eventpoll] ... sssd:1146 /var/log/sssd/sssd.log sssd:1146 anon_inode:[eventpoll] ... NetworkManager:1155 anon_inode:[eventfd] NetworkManager:1155 /var/lib/sss/mc/passwd (deleted) ``` # Variables ## 1. Builtins - `pid` - Process ID (kernel tgid) - `tid` - Thread ID (kernel pid) - `uid` - User ID - `gid` - Group ID - `nsecs` - Nanosecond timestamp - `elapsed` - Nanoseconds since bpftrace initialization - `cpu` - Processor ID - `comm` - Process name - `kstack` - Kernel stack trace - `ustack` - User stack trace - `arg0`, `arg1`, ..., `argN`. - Arguments to the traced function; assumed to be 64 bits wide - `sarg0`, `sarg1`, ..., `sargN`. - Arguments to the traced function (for programs that store arguments on the stack); assumed to be 64 bits wide - `retval` - Return value from traced function - `func` - Name of the traced function - `probe` - Full name of the probe - `curtask` - Current task struct as a u64 - `rand` - Random number as a u32 - `cgroup` - Cgroup ID of the current process - `cpid` - Child pid(u32), only valid with the `-c command` flag - `$1`, `$2`, ..., `$N`, `$#`. - Positional parameters for the bpftrace program Many of these are discussed in other sections (use search). ## 2. `@`, `$`: Basic variables Syntax: ``` @global_name @thread_local_variable_name[tid] $scratch_name ``` bpftrace supports global & per-thread variables (via BPF maps), and scratch variables. Examples: ### 2.1. Global Syntax: `@name` For example, `@start`: ``` # bpftrace -e 'BEGIN { @start = nsecs; } kprobe:do_nanosleep /@start != 0/ { printf("at %d ms: sleep\n", (nsecs - @start) / 1000000); }' Attaching 2 probes... at 437 ms: sleep at 647 ms: sleep at 1098 ms: sleep at 1438 ms: sleep at 1648 ms: sleep ^C @start: 4064438886907216 ``` ### 2.2. Per-Thread: These can be implemented as an associative array keyed on the thread ID. For example, `@start[tid]`: ``` # bpftrace -e 'kprobe:do_nanosleep { @start[tid] = nsecs; } kretprobe:do_nanosleep /@start[tid] != 0/ { printf("slept for %d ms\n", (nsecs - @start[tid]) / 1000000); delete(@start[tid]); }' Attaching 2 probes... slept for 1000 ms slept for 1000 ms slept for 1000 ms slept for 1009 ms slept for 2002 ms [...] ``` ### 2.3. Scratch: Syntax: `$name` For example, `$delta`: ``` # bpftrace -e 'kprobe:do_nanosleep { @start[tid] = nsecs; } kretprobe:do_nanosleep /@start[tid] != 0/ { $delta = nsecs - @start[tid]; printf("slept for %d ms\n", $delta / 1000000); delete(@start[tid]); }' Attaching 2 probes... slept for 1000 ms slept for 1000 ms slept for 1000 ms ``` ## 3. `@[]`: Associative Arrays Syntax: ``` @associative_array_name[key_name] = value @associative_array_name[key_name, key_name2, ...] = value ``` These are implemented using BPF maps. For example, `@start[tid]`: ``` # bpftrace -e 'kprobe:do_nanosleep { @start[tid] = nsecs; } kretprobe:do_nanosleep /@start[tid] != 0/ { printf("slept for %d ms\n", (nsecs - @start[tid]) / 1000000); delete(@start[tid]); }' Attaching 2 probes... slept for 1000 ms slept for 1000 ms slept for 1000 ms [...] ``` ``` # bpftrace -e 'BEGIN { @[1,2] = 3; printf("%d\n", @[1,2]); clear(@); }' Attaching 1 probe... 3 ^C ``` ## 4. `count()`: Frequency Counting This is provided by the count() function: see the [Count](#2-count-count) section. ## 5. `hist()`, `lhist()`: Histograms These are provided by the hist() and lhist() functions. See the [Log2 Histogram](#8-hist-log2-histogram) and [Linear Histogram](#9-lhist-linear-histogram) sections. ## 6. `nsecs`: Timestamps and Time Deltas Syntax: `nsecs` These are implemented using bpf_ktime_get_ns(). Examples: ``` # bpftrace -e 'BEGIN { @start = nsecs; } kprobe:do_nanosleep /@start != 0/ { printf("at %d ms: sleep\n", (nsecs - @start) / 1000000); }' Attaching 2 probes... at 437 ms: sleep at 647 ms: sleep at 1098 ms: sleep at 1438 ms: sleep ^C ``` ## 7. `kstack`: Stack Traces, Kernel Syntax: `kstack` This builtin is an alias to [`kstack()`](#15-kstack-stack-traces-kernel). Examples: ``` # bpftrace -e 'kprobe:ip_output { @[kstack] = count(); }' Attaching 1 probe... [...] @[ 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 @[ ip_output+1 tcp_transmit_skb+1308 tcp_write_xmit+482 __tcp_push_pending_frames+45 tcp_sendmsg_locked+2637 tcp_sendmsg+39 sock_sendmsg+48 sock_write_iter+135 __vfs_write+247 vfs_write+179 sys_write+82 entry_SYSCALL_64_fastpath+30 ]: 9048 @[ ip_output+1 tcp_transmit_skb+1308 tcp_write_xmit+482 tcp_tasklet_func+348 tasklet_action+241 __do_softirq+239 irq_exit+174 do_IRQ+74 ret_from_intr+0 cpuidle_enter_state+159 do_idle+389 cpu_startup_entry+111 start_secondary+398 secondary_startup_64+165 ]: 11430 ``` ## 8. `ustack`: Stack Traces, User Syntax: `ustack` This builtin is an alias to [`ustack()`](#16-ustack-stack-traces-user). Examples: ``` # bpftrace -e 'kprobe:do_sys_open /comm == "bash"/ { @[ustack] = count(); }' Attaching 1 probe... ^C @[ __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 @[ __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+89 _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 ]: 18 ``` Note that for this example to work, bash had to be recompiled with frame pointers. ## 9. `$1`, ..., `$N`, `$#`: Positional Parameters Syntax: `$1`, `$2`, ..., `$N`, `$#` These are the positional parameters to the bpftrace program, also referred to as command line arguments. If the parameter is numeric (entirely digits), it can be used as a number. If it is non-numeric, it must be used as a string in the `str()` call. If a parameter is used that was not provided, it will default to zero for numeric context, and "" for string context. Positional parameters may also be used in probe argument and will be treated as a string parameter. If a positional parameter is used in `str()`, it is interpreted as a pointer to the actual given string literal, which allows to do pointer arithmetic on it. Only addition of a single constant, less or equal to the length of the supplied string, is allowed. `$#` returns the number of positional arguments supplied. This allows scripts to be written that use basic arguments to change their behavior. If you develop a script that requires more complex argument processing, it may be better suited for bcc instead, which supports Python's argparse and completely custom argument processing. One-liner examples: ``` # bpftrace -e 'BEGIN { printf("I got %d, %s (%d args)\n", $1, str($2), $#); }' 42 "hello" Attaching 1 probe... I got 42, hello (2 args) # bpftrace -e 'BEGIN { printf("%s\n", str($1 + 1)) }' "hello" Attaching 1 probe... ello ``` Script example, bsize.d: ``` #!/usr/local/bin/bpftrace BEGIN { printf("Tracing block I/O sizes > %d bytes\n", $1); } tracepoint:block:block_rq_issue /args->bytes > $1/ { @ = hist(args->bytes); } ``` When run with a 65536 argument: ``` # ./bsize.bt 65536 Attaching 2 probes... Tracing block I/O sizes > 65536 bytes ^C @: [512K, 1M) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| ``` It has passed the argument in as $1, and used it as a filter. With no arguments, $1 defaults to zero: ``` # ./bsize.bt Attaching 2 probes... Tracing block I/O sizes > 0 bytes ^C @: [4K, 8K) 115 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 35 |@@@@@@@@@@@@@@@ | [16K, 32K) 5 |@@ | [32K, 64K) 3 |@ | [64K, 128K) 1 | | [128K, 256K) 0 | | [256K, 512K) 0 | | [512K, 1M) 1 | | ``` # Functions ## 1. Builtins - `printf(char *fmt, ...)` - Print formatted - `time(char *fmt)` - Print formatted time - `join(char *arr[] [, char *delim])` - Print the array - `str(char *s [, int length])` - Returns the string pointed to by s - `ksym(void *p)` - Resolve kernel address - `usym(void *p)` - Resolve user space address - `kaddr(char *name)` - Resolve kernel symbol name - `uaddr(char *name)` - Resolve user-level symbol name - `reg(char *name)` - Returns the value stored in the named register - `system(char *fmt)` - Execute shell command - `exit()` - Quit bpftrace - `cgroupid(char *path)` - Resolve cgroup ID - `kstack([StackMode mode, ][int level])` - Kernel stack trace - `ustack([StackMode mode, ][int level])` - User stack trace - `ntop([int af, ]int|char[4|16] addr)` - Convert IP address data to text - `cat(char *filename)` - Print file content - `signal(char[] signal | u32 signal)` - Send a signal to the current task - `strncmp(char *s1, char *s2, int length)` - Compare first n characters of two strings - `override(u64 rc)` - Override return value - `buf(void *d [, int length])` - Returns a hex-formatted string of the data pointed to by d - `sizeof(...)` - Return size of a type or expression - `print(...)` - Print a non-map value with default formatting - `strftime(char *format, int nsecs)` - Return a formatted timestamp - `path(struct path *path)` - Return full path - `uptr(void *p)` - Annotate as userspace pointer - `kptr(void *p)` - Annotate as kernelspace pointer - `macaddr(char[6] addr)` - Convert MAC address data Some of these are asynchronous: the kernel queues the event, but some time later (milliseconds) it is processed in user-space. The asynchronous actions are: `printf()`, `time()`, and `join()`. Both `ksym()` and `usym()`, as well as the variables `kstack` and `ustack`, record addresses synchronously, but then do symbol translation asynchronously. A selection of these are discussed in the following sections. ## 2. `printf()`: Printing Syntax: `printf(fmt, args)` This behaves like printf() from C and other languages, with a limited set of format characters. Example: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s called %s\n", comm, str(args->filename)); }' Attaching 1 probe... bash called /bin/ls bash called /usr/bin/man man called /apps/nflx-bash-utils/bin/preconv man called /usr/local/sbin/preconv man called /usr/local/bin/preconv man called /usr/sbin/preconv man called /usr/bin/preconv man called /apps/nflx-bash-utils/bin/tbl [...] ``` ## 3. `time()`: Time Syntax: `time(fmt)` This prints the current time using the format string supported by libc `strftime(3)`. ``` # bpftrace -e 'kprobe:do_nanosleep { time("%H:%M:%S\n"); }' 07:11:03 07:11:09 ^C ``` If a format string is not provided, it defaults to "%H:%M:%S\n". Note that this builtin is asynchronous. The printed timestamp is the time at which userspace has processed the queued up event, _not_ the time at which the bpf prog calls `time()`. For a more precise timestamp, see [strftime()](#24-strftime-formatted-timestamp). ## 4. `join()`: Join Syntax: `join(char *arr[] [, char *delim])` This joins the array of strings with a space character, and prints it out, separated by delimiters. The default delimiter, if none is provided, is the space character. This current version does not return a string, so it cannot be used as an argument in printf(). Example: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }' Attaching 1 probe... ls --color=auto man ls preconv -e UTF-8 preconv -e UTF-8 preconv -e UTF-8 preconv -e UTF-8 preconv -e UTF-8 tbl [...] ``` ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv, ","); }' Attaching 1 probe... ls,--color=auto man,ls preconv,-e,UTF-8 preconv,-e,UTF-8 preconv,-e,UTF-8 preconv,-e,UTF-8 preconv,-e,UTF-8 tbl [...] ``` ## 5. `str()`: Strings Syntax: `str(char *s [, int length])` Returns the string pointed to by s. `length` can be used to limit the size of the read, and/or introduce a null-terminator. By default, the string will have size 64 bytes (tuneable using [env var `BPFTRACE_STRLEN`](#91-bpftrace_strlen)). Examples: We can take the `args->filename` of `sys_enter_execve` (a `const char *filename`), and read the string to which it points. This string can be provided as an argument to printf(): ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s called %s\n", comm, str(args->filename)); }' Attaching 1 probe... bash called /bin/ls bash called /usr/bin/man man called /apps/nflx-bash-utils/bin/preconv man called /usr/local/sbin/preconv man called /usr/local/bin/preconv man called /usr/sbin/preconv man called /usr/bin/preconv man called /apps/nflx-bash-utils/bin/tbl [...] ``` We can trace strings that are displayed in a bash shell. Some length tuning is employed, because: - sys_enter_write()'s `args->buf` does not point to null-terminated strings - we use the length parameter to limit how many bytes to read of the pointed-to string - sys_enter_write()'s `args->buf` contains messages larger than 64 bytes - we increase BPFTRACE_STRLEN to accommodate the large messages ``` # BPFTRACE_STRLEN=200 bpftrace -e 'tracepoint:syscalls:sys_enter_write /pid == 23506/ { printf("<%s>\n", str(args->buf, args->count)); }' # type pwd into terminal 23506

# press enter in terminal 23506 < > ``` ## 6. `ksym()`: Symbol resolution, kernel-level Syntax: `ksym(addr)` Examples: ``` # bpftrace -e 'kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); }' Attaching 1 probe... do_nanosleep do_nanosleep ``` ## 7. `usym()`: Symbol resolution, user-level Syntax: `usym(addr)` Examples: ``` # bpftrace -e 'uprobe:/bin/bash:readline { printf("%s\n", usym(reg("ip"))); }' Attaching 1 probe... readline readline readline ^C ``` ## 8. `kaddr()`: Address resolution, kernel-level Syntax: `kaddr(char *name)` Examples: ``` # bpftrace -e 'BEGIN { printf("%s\n", str(*kaddr("usbcore_name"))); }' Attaching 1 probe... usbcore ^C ``` This is printing the `usbcore_name` string from drivers/usb/core/usb.c: ``` const char *usbcore_name = "usbcore"; ``` ## 9. `uaddr()`: Address resolution, user-level Syntax: - `u64 *uaddr(symbol)` (default) - `u64 *uaddr(symbol)` - `u32 *uaddr(symbol)` - `u16 *uaddr(symbol)` - `u8 *uaddr(symbol)` Supported Probe Types: - u(ret)probes - USDT **Does not work with ASLR, see issue [#75](https://github.com/iovisor/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 `u64*`. 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 (`u8*`, `u16*`, `u32*` or `u64*` resp.). As ELF does not contain type info the type is always assumed to be unsigned. Examples: ``` # bpftrace -e 'uprobe:/bin/bash:readline { printf("PS1: %s\n", str(*uaddr("ps1_prompt"))); }' Attaching 1 probe... PS1: \[\e[34;1m\]\u@\h:\w>\[\e[0m\] PS1: \[\e[34;1m\]\u@\h:\w>\[\e[0m\] ^C ``` This is printing the `ps1_prompt` string from /bin/bash, whenever a `readline()` function is executed. ## 10. `reg()`: Registers Syntax: `reg(char *name)` Examples: ``` # bpftrace -e 'kprobe:tcp_sendmsg { @[ksym(reg("ip"))] = count(); }' Attaching 1 probe... ^C @[tcp_sendmsg]: 7 ``` See src/arch/x86_64.cpp for the register name list. ## 11. `system()`: System Syntax: `system(fmt)` This runs the provided command at the shell. For example: ``` # bpftrace --unsafe -e 'kprobe:do_nanosleep { system("ps -p %d\n", pid); }' Attaching 1 probe... PID TTY TIME CMD 1339 ? 00:00:15 iscsid PID TTY TIME CMD 1339 ? 00:00:15 iscsid PID TTY TIME CMD 1518 ? 00:01:07 irqbalance PID TTY TIME CMD 1339 ? 00:00:15 iscsid ^C ``` This can be useful to execute commands or a shell script when an instrumented event happens. Note this is an unsafe function. To use it, bpftrace must be run with `--unsafe`. ## 12. `exit()`: Exit Syntax: `exit()` This exits bpftrace, and can be combined with an interval probe to record statistics for a certain duration. Example: ``` # bpftrace -e 'kprobe:do_sys_open { @opens = count(); } interval:s:1 { exit(); }' Attaching 2 probes... @opens: 119 ``` ## 13. `cgroupid()`: Resolve cgroup ID Syntax: `cgroupid(char *path)` This returns a cgroup ID of a specific cgroup, and can be combined with the `cgroup` builtin to filter the tasks that belong to the specific cgroup, for example: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat /cgroup == cgroupid("/sys/fs/cgroup/unified/mycg")/ { printf("%s\n", str(args->filename)); }': Attaching 1 probe... /etc/ld.so.cache /lib64/libc.so.6 /usr/lib/locale/locale-archive /etc/shadow ^C ``` And in other terminal: ``` # echo $$ > /sys/fs/cgroup/unified/mycg/cgroup.procs # cat /etc/shadow ``` ## 14. `ntop()`: Convert IP address data to text Syntax: `ntop([int af, ]int|char[4|16] addr)` This 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 explicitly as the first parameter. Examples: A simple example of ntop with an ipv4 hex-encoded literal: ``` bpftrace -e 'BEGIN { printf("%s\n", ntop(0x0100007f));}' 127.0.0.1 ^C ``` Same example as before, but passing the address type explicitly to ntop: ``` bpftrace -e '#include BEGIN { printf("%s\n", ntop(AF_INET, 0x0100007f));}' 127.0.0.1 ^C ``` A less trivial example of this usage, tracing tcp state changes, and printing the destination IPv6 address: ``` bpftrace -e 'tracepoint:tcp:tcp_set_state { printf("%s\n", ntop(args->daddr_v6)) }' Attaching 1 probe... ::ffff:216.58.194.164 ::ffff:216.58.194.164 ::ffff:216.58.194.164 ::ffff:216.58.194.164 ::ffff:216.58.194.164 ^C ``` And initiate a connection to this (or any) address in another terminal: ``` curl www.google.com ``` ## 15. `kstack()`: Stack Traces, Kernel Syntax: `kstack([StackMode mode, ][int limit])` These are implemented using BPF stack maps. Examples: ``` # bpftrace -e 'kprobe:ip_output { @[kstack()] = count(); }' Attaching 1 probe... [...] @[ 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 @[ ip_output+1 tcp_transmit_skb+1308 tcp_write_xmit+482 __tcp_push_pending_frames+45 tcp_sendmsg_locked+2637 tcp_sendmsg+39 sock_sendmsg+48 sock_write_iter+135 __vfs_write+247 vfs_write+179 sys_write+82 entry_SYSCALL_64_fastpath+30 ]: 9048 @[ ip_output+1 tcp_transmit_skb+1308 tcp_write_xmit+482 tcp_tasklet_func+348 tasklet_action+241 __do_softirq+239 irq_exit+174 do_IRQ+74 ret_from_intr+0 cpuidle_enter_state+159 do_idle+389 cpu_startup_entry+111 start_secondary+398 secondary_startup_64+165 ]: 11430 ``` Sampling only three frames from the stack (limit = 3): ``` # bpftrace -e 'kprobe:ip_output { @[kstack(3)] = count(); }' Attaching 1 probe... [...] @[ ip_output+1 tcp_transmit_skb+1308 tcp_write_xmit+482 ]: 22186 ``` You can also choose a different output format. Available formats are `bpftrace` and `perf`: ``` # bpftrace -e 'kprobe:do_mmap { @[kstack(perf)] = count(); }' Attaching 1 probe... [...] @[ ffffffffb4019501 do_mmap+1 ffffffffb401700a sys_mmap_pgoff+266 ffffffffb3e334eb sys_mmap+27 ffffffffb3e03ae3 do_syscall_64+115 ffffffffb4800081 entry_SYSCALL_64_after_hwframe+61 ]: 22186 ``` It's also possible to use a different output format and limit the number of frames: ``` # bpftrace -e 'kprobe:do_mmap { @[kstack(perf, 3)] = count(); }' Attaching 1 probe... [...] @[ ffffffffb4019501 do_mmap+1 ffffffffb401700a sys_mmap_pgoff+266 ffffffffb3e334eb sys_mmap+27 ]: 22186 ``` ## 16. `ustack()`: Stack Traces, User Syntax: `ustack([StackMode mode, ][int limit])` These are implemented using BPF stack maps. Examples: ``` # bpftrace -e 'kprobe:do_sys_open /comm == "bash"/ { @[ustack()] = count(); }' Attaching 1 probe... ^C @[ __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 @[ __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+89 _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 ]: 18 ``` Sampling only six frames from the stack (limit = 6): ``` # bpftrace -e 'kprobe:do_sys_open /comm == "bash"/ { @[ustack(6)] = count(); }' Attaching 1 probe... ^C @[ __open_nocancel+65 command_word_completion_function+3604 rl_completion_matches+370 bash_default_completion+540 attempt_shell_completion+2092 gen_completion_matches+82 ]: 27 ``` You can also choose a different output format. Available formats are `bpftrace` and `perf`: ``` # bpftrace -e 'uprobe:bash:readline { printf("%s\n", ustack(perf)); }' Attaching 1 probe... 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) 5649fee2cd36 shell_getc+469 (/home/mmarchini/bash/bash/bash) 5649fee2e527 read_token+251 (/home/mmarchini/bash/bash/bash) 5649fee2d9e2 yylex+192 (/home/mmarchini/bash/bash/bash) 5649fee286fd yyparse+777 (/home/mmarchini/bash/bash/bash) 5649fee27dd6 parse_command+54 (/home/mmarchini/bash/bash/bash) ``` It's also possible to use a different output format and limit the number of frames: ``` # bpftrace -e 'uprobe:bash:readline { printf("%s\n", ustack(perf, 3)); }' Attaching 1 probe... 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) ``` Note that for these examples to work, bash had to be recompiled with frame pointers. ## 17. `cat()`: Print file content Syntax: `cat(filename)` This prints the file content. For example: ``` # bpftrace -e 't:syscalls:sys_enter_execve { printf("%s ", str(args->filename)); cat("/proc/loadavg"); }' Attaching 1 probe... /usr/libexec/grepconf.sh 3.18 2.90 2.94 2/977 30138 /usr/bin/grep 3.18 2.90 2.94 4/978 30139 /usr/bin/flatpak 3.18 2.90 2.94 2/980 30143 /usr/bin/grep 3.18 2.90 2.94 3/977 30144 /usr/bin/sed 3.18 2.90 2.94 7/978 30146 /usr/bin/tclsh 3.18 2.90 2.94 5/978 30150 /usr/bin/manpath 3.18 2.90 2.94 2/978 30152 /bin/ps 3.18 2.90 2.94 2/979 30155 ^C ``` The `cat()` builtin also supports a format string as argument: ``` ./bpftrace -e 'tracepoint:syscalls:sys_enter_sendmsg { printf("%s => ", comm); cat("/proc/%d/cmdline", pid); printf("\n") }' Attaching 1 probe... Gecko_IOThread => /usr/lib64/firefox/firefox Gecko_IOThread => /usr/lib64/firefox/firefox Gecko_IOThread => /usr/lib64/firefox/firefox Gecko_IOThread => /usr/lib64/firefox/firefox Gecko_IOThread => /usr/lib64/firefox/firefox Gecko_IOThread => /usr/lib64/firefox/firefox Gecko_IOThread => /usr/lib64/firefox/firefox ^C ``` ## 18. `signal()`: Send a signal to current task Syntax: - `signal(u32 signal)` - `signal("SIG")` Kernel: 5.3 Supported Probe Types: - k(ret)probes - u(ret)probes - USDT - profile `signal` sends the specified signal to the current task: ``` # bpftrace -e 'kprobe:__x64_sys_execve /comm == "bash"/ { signal(5); }' --unsafe $ ls Trace/breakpoint trap (core dumped) ``` The signal can also be specified using a name, similar to the `kill(1)` command: ``` # bpftrace -e 'k:f { signal("KILL"); }' # bpftrace -e 'k:f { signal("SIGINT"); }' ``` ## 19. `strncmp()`: Compare first n characters of two strings Syntax: `strncmp(char *s1, char *s2, int length)` Return zero if the first `length` characters in `s1` and `s2` are equal, and non-zero otherwise. Examples: ``` bpftrace -e 't:syscalls:sys_enter_* /strncmp("mpv", comm, 3) == 0/ { @[comm, probe] = count() }' Attaching 320 probes... [...] @[mpv/vo, tracepoint:syscalls:sys_enter_rt_sigaction]: 238 @[mpv:gdrv0, tracepoint:syscalls:sys_enter_futex]: 680 @[mpv/ao, tracepoint:syscalls:sys_enter_write]: 1022 @[mpv, tracepoint:syscalls:sys_enter_ioctl]: 2677 @[mpv:cs0, tracepoint:syscalls:sys_enter_ioctl]: 2889 @[mpv/vo, tracepoint:syscalls:sys_enter_read]: 2993 @[mpv/demux, tracepoint:syscalls:sys_enter_futex]: 4745 @[mpv, tracepoint:syscalls:sys_enter_write]: 6936 @[mpv/vo, tracepoint:syscalls:sys_enter_futex]: 7662 @[mpv:cs0, tracepoint:syscalls:sys_enter_futex]: 8127 @[mpv/lua script , tracepoint:syscalls:sys_enter_futex]: 10150 @[mpv/vo, tracepoint:syscalls:sys_enter_poll]: 10241 @[mpv/vo, tracepoint:syscalls:sys_enter_recvmsg]: 15018 @[mpv, tracepoint:syscalls:sys_enter_getpid]: 31178 @[mpv, tracepoint:syscalls:sys_enter_futex]: 403868 ``` ## 20. `override()`: Override return value Syntax: `override(u64 rc)` Kernel: 4.16 Supported Probe Types: kprobes The probed function will not be executed, instead a helper will be executed that will just return `rc`. ``` # bpftrace -e 'k:__x64_sys_getuid /comm == "id"/ { override(2<<21); }' --unsafe -c id 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' ``` ## 21. `buf()`: Buffers Syntax: `buf(void *d [, int length])` Returns a hex-formatted string of the data pointed to by `d` that is safe to print. Because the length of the buffer cannot always be inferred, the `length` parameter may be provided to limit the number of bytes that are read. By default, the maximum number of bytes is 64, but this can be tuned using the [`BPFTRACE_STRLEN`](#91-bpftrace_strlen) environment variable. Bytes with values >=32 and <=126 are printed using their ASCII character, other bytes are printed in hex form (e.g. `\x00`). For example, we can take the `buff` parameter (`void *`) of `sys_enter_sendto`, read the number of bytes specified by `len` (`size_t`), and format the bytes in hexadecimal so that they don't corrupt the terminal display. The resulting string can be provided as an argument to printf() using the `%r` format specifier: ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_sendto { printf("Datagram bytes: %r\n", buf(args->buff, args->len)); }' -c 'ping 8.8.8.8 -c1' Attaching 1 probe... PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. Datagram bytes: \x08\x00+\xb9\x06b\x00\x01Aen^\x00\x00\x00\x00KM\x0c\x00\x00\x00\x00\x00\x10\x11 \x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&'()*+,-./01234567 64 bytes from 8.8.8.8: icmp_seq=1 ttl=52 time=19.4 ms --- 8.8.8.8 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 19.426/19.426/19.426/0.000 ms ``` ## 22. `sizeof()`: Size of type or expression Syntax: - `sizeof(TYPE)` - `sizeof(EXPRESSION)` Returns size of the argument in bytes. Similar to C/C++ `sizeof` operator. Note that the expression does not get evaluated. Examples: ``` # bpftrace -e 'struct Foo { int x; char c; } BEGIN { printf("%d\n", sizeof(struct Foo)); }' Attaching 1 probe... 8 # bpftrace -e 'struct Foo { int x; char c; } BEGIN { printf("%d\n", sizeof(((struct Foo)0).c)); }' Attaching 1 probe... 1 # bpftrace -e 'BEGIN { printf("%d\n", sizeof(1 == 1)); }' Attaching 1 probe... 8 # bpftrace -e 'BEGIN { printf("%d\n", sizeof(struct task_struct)); }' Attaching 1 probe... 13120 # bpftrace -e 'BEGIN { $x = 3; printf("%d\n", sizeof($x)); }' Attaching 1 probe... 8 ``` ## 23. `print()`: Print Value Syntax: ```print(value)``` The `print()` function can print a non-map value with default formatting. For example, local variables and most builtins can be printed: ``` # bpftrace -e 'BEGIN { $t = (1, "string"); print(123); print($t); print(comm) }' Attaching 1 probe... 123 (1, string) bpftrace ^C ``` It is important to note that printing values is different than printing maps. Both printing maps and printing values are asynchronous: the kernel queues the event but some time later it is processed in userspace. For values, the event contains the memcopy'd value so the value at `print()` invocation time will be printed. However for maps, only the handle to the map is queued up, so the printed map may be different than the map at `print()` invocation. ## 24. `strftime()`: Formatted timestamp Syntax: - `strftime(const char *format, int nsecs)` This returns a formatted timestamp that is printable with `printf`. The format string must be supported by `strftime(3)`. `nsecs` is nanoseconds since boot, typically derived from [nsecs](#6-nsecs-timestamps-and-time-deltas). Use format specifier "%s" when printing the return value. Note that `strftime` does not actually return a string in bpf (kernel), the formatting happens in userspace. bpftrace also supports the following format string extensions: Specifier | Description ---- | ----------- `%f` | Microsecond as a decimal number, zero-padded on the left Examples: ``` # bpftrace -e 'i:s:1 { printf("%s\n", strftime("%H:%M:%S", nsecs)); }' Attaching 1 probe... 13:11:22 13:11:23 13:11:24 13:11:25 13:11:26 ^C # bpftrace -e 'i:s:1 { printf("%s\n", strftime("%H:%M:%S:%f", nsecs)); }' Attaching 1 probe... 15:22:24:104033 ^C ``` ## 25. `path()`: Return full path Syntax: - `path(struct path *path)` Return full path referenced by struct path pointer in argument. There's list of allowed kernel functions, that can use this helper in probe. Examples: ``` # bpftrace -e 'kfunc:filp_close { printf("%s\n", path(args->filp->f_path)); }' Attaching 1 probe... /proc/sys/net/ipv6/conf/eno2/disable_ipv6 /proc/sys/net/ipv6/conf/eno2/use_tempaddr socket:[23276] /proc/sys/net/ipv6/conf/eno2/disable_ipv6 socket:[17655] /sys/devices/pci0000:00/0000:00:1c.5/0000:04:00.1/net/eno2/type socket:[38745] /proc/sys/net/ipv6/conf/eno2/disable_ipv6 # bpftrace -e 'kretfunc:dentry_open { printf("%s\n", path(retval->f_path)); }' Attaching 1 probe... /dev/pts/1 -> /dev/pts/1 ``` ## 26. `uptr()`: Annotate userspace pointer Syntax: - `uptr(void *p)` Annotate `p` as a pointer belonging to userspace address space. bpftrace can usually infer the address space of a pointer. However, there are corner cases where inference fails. For example, kernel functions that deal with userspace pointers (a parameter like `const char __user *p`). In these cases, you'll need to annotate the pointer. Examples: ``` # bpftrace -e 'kprobe:do_sys_open { printf("%s\n", str(uptr(arg1))) }' Attaching 1 probe... . state ^C ``` ## 27. `kptr()`: Annotate kernelspace pointer Syntax: - `kptr(void *p)` Annotate `p` as a pointer belonging to kernel address space. Just like `uptr`, you'll generally only need this if bpftrace has inferred the pointer address space incorrectly. ## 28. `macaddr()`: Convert MAC address data to text Syntax: `macaddr(char[6] addr)` This returns the canonical string representation of a MAC address. Example: ``` # bpftrace -e 'kprobe:arp_create { printf("SRC %s, DST %s\n", macaddr(sarg0), macaddr(sarg1)); }' SRC 18:C0:4D:08:2E:BB, DST 74:83:C2:7F:8C:FF ^C ``` # Map Functions Maps are special BPF data types that can be used to store counts, statistics, and histograms. They are also used for some variable types as discussed in the previous section, whenever `@` is used: [globals](#21-global), [per thread variables](#22-per-thread), and [associative arrays](#3--associative-arrays). When bpftrace exits, all maps are printed. For example (the `count()` function is covered in the sections that follow): ``` # bpftrace -e 'kprobe:vfs_read { @[comm] = count(); }' Attaching 1 probe... ^C @[systemd]: 6 @[vi]: 7 @[sshd]: 16 @[snmpd]: 321 @[snmp-pass]: 374 ``` The map was printed after the Ctrl-C to end the program. If you use maps that you do not wish to be automatically printed on exit, you can add an END block that clears the maps. For example: ``` END { clear(@start); } ``` ## 1. Builtins - `count()` - Count the number of times this function is called - `sum(int n)` - Sum the value - `avg(int n)` - Average the value - `min(int n)` - Record the minimum value seen - `max(int n)` - Record the maximum value seen - `stats(int n)` - Return the count, average, and total for this value - `hist(int n)` - Produce a log2 histogram of values of n - `lhist(int n, int min, int max, int step)` - Produce a linear histogram of values of n - `delete(@x[key])` - Delete the map element passed in as an argument - `print(@x[, top [, div]])` - Print the map, optionally the top entries only and with a divisor - `print(value)` - Print a value - `clear(@x)` - Delete all keys from the map - `zero(@x)` - Set all map values to zero Some of these are asynchronous: the kernel queues the event, but some time later (milliseconds) it is processed in user-space. The asynchronous actions are: `print()` on maps, `clear()`, and `zero()`. ## 2. `count()`: Count Syntax: `@counter_name[optional_keys] = count()` This is implemented using a BPF map. For example, `@reads`: ``` # bpftrace -e 'kprobe:vfs_read { @reads = count(); }' Attaching 1 probe... ^C @reads: 119 ``` That shows there were 119 calls to vfs_read() while tracing. This next example includes the `comm` variable as a key, so that the value is broken down by each process name. For example, `@reads[comm]`: ``` # bpftrace -e 'kprobe:vfs_read { @reads[comm] = count(); }' Attaching 1 probe... ^C @reads[sleep]: 4 @reads[bash]: 5 @reads[ls]: 7 @reads[snmp-pass]: 8 @reads[snmpd]: 14 @reads[sshd]: 14 ``` ## 3. `sum()`: Sum Syntax: `@counter_name[optional_keys] = sum(value)` This is implemented using a BPF map. For example, `@bytes[comm]`: ``` # bpftrace -e 'kprobe:vfs_read { @bytes[comm] = sum(arg2); }' Attaching 1 probe... ^C @bytes[bash]: 7 @bytes[sleep]: 4160 @bytes[ls]: 6208 @bytes[snmpd]: 20480 @bytes[snmp-pass]: 65536 @bytes[sshd]: 262144 ``` That is summing requested bytes via the vfs_read() kernel function, which is one of two possible entry points for the read syscall. To see actual bytes read: ``` # bpftrace -e 'kretprobe:vfs_read /retval > 0/ { @bytes[comm] = sum(retval); }' Attaching 1 probe... ^C @bytes[bash]: 5 @bytes[sshd]: 1135 @bytes[systemd-journal]: 1699 @bytes[sleep]: 2496 @bytes[ls]: 4583 @bytes[snmpd]: 35549 @bytes[snmp-pass]: 55681 ``` Now a filter is used to ensure the return value was positive before it is used in the sum(). The return value may be negative in cases of error, as is the case with other functions. Remember this whenever using sum() on a retval. ## 4. `avg()`: Average Syntax: `@counter_name[optional_keys] = avg(value)` This is implemented using a BPF map. For example, `@bytes[comm]`: ``` # bpftrace -e 'kprobe:vfs_read { @bytes[comm] = avg(arg2); }' Attaching 1 probe... ^C @bytes[bash]: 1 @bytes[sleep]: 832 @bytes[ls]: 886 @bytes[snmpd]: 1706 @bytes[snmp-pass]: 8192 @bytes[sshd]: 16384 ``` This is averaging the requested read size. ## 5. `min()`: Minimum Syntax: `@counter_name[optional_keys] = min(value)` This is implemented using a BPF map. For example, `@bytes[comm]`: ``` # bpftrace -e 'kprobe:vfs_read { @bytes[comm] = min(arg2); }' Attaching 1 probe... ^C @bytes[bash]: 1 @bytes[systemd-journal]: 8 @bytes[snmpd]: 64 @bytes[ls]: 832 @bytes[sleep]: 832 @bytes[snmp-pass]: 8192 @bytes[sshd]: 16384 ``` This shows the minimum value seen. ## 6. `max()`: Maximum Syntax: `@counter_name[optional_keys] = max(value)` This is implemented using a BPF map. For example, `@bytes[comm]`: ``` # bpftrace -e 'kprobe:vfs_read { @bytes[comm] = max(arg2); }' Attaching 1 probe... ^C @bytes[bash]: 1 @bytes[systemd-journal]: 8 @bytes[sleep]: 832 @bytes[ls]: 1024 @bytes[snmpd]: 4096 @bytes[snmp-pass]: 8192 @bytes[sshd]: 16384 ``` This shows the maximum value seen. ## 7. `stats()`: Stats Syntax: `@counter_name[optional_keys] = stats(value)` This is implemented using a BPF map. For example, `@bytes[comm]`: ``` # bpftrace -e 'kprobe:vfs_read { @bytes[comm] = stats(arg2); }' Attaching 1 probe... ^C @bytes[bash]: count 7, average 1, total 7 @bytes[sleep]: count 5, average 832, total 4160 @bytes[ls]: count 7, average 886, total 6208 @bytes[snmpd]: count 18, average 1706, total 30718 @bytes[snmp-pass]: count 12, average 8192, total 98304 @bytes[sshd]: count 15, average 16384, total 245760 ``` This stats() function returns three statistics: the count of events, the average for the argument value, and the total of the argument value. This is similar to using count(), avg(), and sum(). ## 8. `hist()`: Log2 Histogram Syntax: ``` @histogram_name[optional_key] = hist(value) ``` This is implemented using a BPF map. Examples: ### 8.1. Power-Of-2: ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = hist(retval); }' Attaching 1 probe... ^C @bytes: (..., 0) 117 |@@@@@@@@@@@@ | [0] 5 | | [1] 325 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [2, 4) 6 | | [4, 8) 3 | | [8, 16) 495 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [16, 32) 35 |@@@ | [32, 64) 25 |@@ | [64, 128) 21 |@@ | [128, 256) 1 | | [256, 512) 3 | | [512, 1K) 2 | | [1K, 2K) 1 | | [2K, 4K) 2 | | ``` ### 8.2. Power-Of-2 By Key: ``` # bpftrace -e 'kretprobe:do_sys_open { @bytes[comm] = hist(retval); }' Attaching 1 probe... ^C @bytes[snmp-pass]: [4, 8) 6 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @bytes[ls]: [2, 4) 9 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @bytes[snmpd]: [1] 1 |@@@@ | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 4 |@@@@@@@@@@@@@@@@@@ | [16, 32) 11 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @bytes[irqbalance]: (..., 0) 15 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [0] 0 | | [1] 0 | | [2, 4) 0 | | [4, 8) 21 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| ``` ## 9. `lhist()`: Linear Histogram Syntax: ``` @histogram_name[optional_key] = lhist(value, min, max, step) ``` This is implemented using a BPF map. `min` must be non-negative. Examples: ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 10000, 1000); }' Attaching 1 probe... ^C @bytes: [0, 1000) 480 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1000, 2000) 49 |@@@@@ | [2000, 3000) 12 |@ | [3000, 4000) 39 |@@@@ | [4000, 5000) 267 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` ## 10. `print()`: Print Map Syntax: ```print(@map [, top [, divisor]])``` The `print()` function will print a map, similar to the automatic printing when bpftrace ends. Two optional arguments can be provided: a top number, so that only the top number of entries are printed, and a divisor, which divides the value. A couple of examples will explain their use. As an example of top, tracing `vfs` operations and printing the top 5: ``` # bpftrace -e 'kprobe:vfs_* { @[func] = count(); } END { print(@, 5); clear(@); }' Attaching 54 probes... ^C @[vfs_getattr]: 91 @[vfs_getattr_nosec]: 92 @[vfs_statx_fd]: 135 @[vfs_open]: 188 @[vfs_read]: 405 ``` The final `clear()` is used to prevent printing the map automatically on exit. As an example of divisor, summing total time in vfs_read() by process name as milliseconds: ``` # bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ {@ms[pid] = sum(nsecs - @start[tid]); delete(@start[tid]); } END { print(@ms, 0, 1000000); clear(@ms); clear(@start); }' ``` This one-liner sums the vfs_read() durations as nanoseconds, and then does the division to milliseconds when printing. Without this capability, should one try to divide to milliseconds when summing (eg, `sum((nsecs - @start[tid]) / 1000000)`), the value would often be rounded to zero, and not accumulate as it should. Note that printing maps is different than printing values. See the explanation in [`print()`: Print Value](#23-print-print-value). # Output ## 1. `printf()`: Per-Event Output Syntax: `printf(char *format, arguments)` Per-event details can be printed using `print()`. Examples: ``` # bpftrace -e 'kprobe:do_nanosleep { printf("sleep by %d\n", tid); }' Attaching 1 probe... sleep by 3669 sleep by 1396 sleep by 3669 sleep by 1396 [...] ``` ## 2. `interval`: Interval output Syntax: `interval:s:duration_seconds` Examples: ``` # bpftrace -e 'kprobe:do_sys_open { @opens = @opens + 1; } interval:s:1 { printf("opens/sec: %d\n", @opens); @opens = 0; }' Attaching 2 probes... opens/sec: 16 opens/sec: 2 opens/sec: 3 opens/sec: 15 opens/sec: 8 opens/sec: 2 ^C @opens: 2 ``` ## 3. `hist()`, `print()`: Histogram Printing Declared histograms are automatically printed out on program termination. See [5. Histograms](#5-histograms) for declarations. Examples: ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = hist(retval); }' Attaching 1 probe... ^C @bytes: (..., 0) 117 |@@@@@@@@@@@@ | [0] 5 | | [1] 325 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [2, 4) 6 | | [4, 8) 3 | | [8, 16) 495 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [16, 32) 35 |@@@ | [32, 64) 25 |@@ | [64, 128) 21 |@@ | [128, 256) 1 | | [256, 512) 3 | | [512, 1K) 2 | | [1K, 2K) 1 | | [2K, 4K) 2 | | ``` Histograms can also be printed on-demand, using the `print()` function. Eg: ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = hist(retval); } interval:s:1 { print(@bytes); clear(@bytes); }' [...] ``` # BTF Support If kernel has BTF, kernel types are automatically available and there is no need to include additional headers to use them. Requirements for using BTF: - 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+) See [kernel documentation](https://www.kernel.org/doc/html/latest/bpf/btf.html) for more information on BTF. Beware that BTF types are not available to a bpftrace program if it contains a user-defined type that redefines some BTF type. Here, "user-defined types" are also types introduced via included headers. Therefore, if you include a kernel header in your bpftrace program, it is very likely that it will define some kernel type and that BTF won't be available to your program (and you'll have to define/include all necessary types manually). # Advanced 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](https://github.com/iovisor/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. 1. Split your program over multiple probes. 1. Check the status of the BPF stack limit in Linux (it may be increased in the future, maybe as a tuneabe). 1. (advanced): Run -d and examine the LLVM IR, and look for ways to optimize src/ast/codegen_llvm.cpp. ## 2. Kernel headers not found bpftrace requires kernel headers for certain features, which are searched for by default in: ```bash /lib/modules/$(uname -r) ``` The default search directory can be overridden using the environment variable `BPFTRACE_KERNEL_SOURCE`. bpftrace-0.14.0/docs/release_process.md000066400000000000000000000043261413460502400200160ustar00rootroot00000000000000# Release procedure This document describes how to release a new bpftrace version. The "release manager" (RM) can be one or more person. Usually whoever is motivated enough to drive a release. ## Branching model In the usual case, we release directly from master. Reasoning is that bpftrace isn't a huge project yet so complicated branching models and release strategies more get in the way than provide order. If master is really busy or really buggy, the RM can choose to cut a release branch (titled `X.Y.Z_release`) to try and stabilize the code without including work in progress into the release. ## 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. ## 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/ . ## Tagging a release You must do these things to formally release a version: 1. Mark the release in the CHANGELOG by replacing the `## Unreleased` header with `## [VERSION] date`. 1. Update `bpftrace_VERSION_MAJOR`, `bpftrace_VERSION_MINOR`, and `bpftrace_VERSION_PATCH` in `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. Once the release tag pipeline has finished, extract the bpftrace binary from the release build on and attach it to the release. - `docker run -v $(pwd):/output quay.io/iovisor/bpftrace:vXX.YY.ZZ /bin/bash -c "cp /usr/bin/bpftrace /output"` 1. Run `scripts/create-assets.sh` from bpftrace root dir and attach the generated archives to the release. bpftrace-0.14.0/docs/tutorial_one_liners.md000066400000000000000000000414541413460502400207230ustar00rootroot00000000000000# The bpftrace One-Liner Tutorial This teaches you bpftrace for Linux in 12 easy lessons, where each lesson is a one-liner you can try running. This series of one-liners introduces concepts which are summarized as bullet points. For a full reference to bpftrace, see the [Man page](man/adoc/bpftrace.adoc) Contributed by Brendan Gregg, Netflix (2018), based on his FreeBSD [DTrace Tutorial](https://wiki.freebsd.org/DTrace/Tutorial). # Lesson 1. Listing Probes ``` bpftrace -l 'tracepoint:syscalls:sys_enter_*' ``` "bpftrace -l" lists all probes, and a search term can be added. - A probe is an instrumentation point for capturing event data. - The supplied search term supports wildcards/globs (`*` and `?`) - "bpftrace -l" can also be piped to grep(1) for full regular expression searching. # Lesson 2. Hello World ``` # bpftrace -e 'BEGIN { printf("hello world\n"); }' Attaching 1 probe... hello world ^C ``` This prints a welcome message. Run it, then hit Ctrl-C to end. - The word `BEGIN` is a special probe that fires at the start of the program (like awk's BEGIN). You can use it to set variables and print headers. - An action can be associated with probes, in { }. This example calls printf() when the probe fires. # Lesson 3. File Opens ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }' Attaching 1 probe... snmp-pass /proc/cpuinfo snmp-pass /proc/stat snmpd /proc/net/dev snmpd /proc/net/if_inet6 ^C ``` This traces file opens as they happen, and we're printing the process name and pathname. - It begins with the probe `tracepoint:syscalls:sys_enter_openat`: this is the tracepoint probe type (kernel static tracing), and is instrumenting when the `openat()` syscall begins (is entered). Tracepoints are preferred over kprobes (kernel dynamic tracing, introduced in lesson 6), since tracepoints have stable API. Note: In modern Linux systems (glibc >= 2.26) the `open` wrapper always calls the `openat` syscall. - `comm` is a builtin variable that has the current process's name. Other similar builtins include pid and tid. - `args` is a pointer to 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` dereferences the `args` struct and gets the value of the `filename` member. - `str()` turns a pointer into the string it points to. # Lesson 4. Syscall Counts By Process ``` bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' Attaching 1 probe... ^C @[bpftrace]: 6 @[systemd]: 24 @[snmp-pass]: 96 @[sshd]: 125 ``` This summarizes syscalls by process name, printing a report on Ctrl-C. - @: This denotes a special variable type called a map, which can store and summarize data in different ways. You can add an optional variable name after the @, eg "@num", either to improve readability, or to differentiate between more than one map. - []: The optional brackets allow a key to be set for the map, much like an associative array. - count(): This is a map function – the way it is populated. count() counts the number of times it is called. Since this is saved by comm, the result is a frequency count of system calls by process name. Maps are automatically printed when bpftrace ends (eg, via Ctrl-C). # Lesson 5. Distribution of read() Bytes ``` # bpftrace -e 'tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args->ret); }' Attaching 1 probe... ^C @bytes: [0, 1] 12 |@@@@@@@@@@@@@@@@@@@@ | [2, 4) 18 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 30 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 19 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [128, 256) 1 |@ ``` This summarizes the return value of the sys_read() kernel function for PID 18644, printing it as a histogram. - /.../: This is a filter (aka predicate), which acts as a filter for the action. The action is only executed if the filtered expression is true, in this case, only for the process ID 18644. Boolean operators are supported ("&&", "||"). - ret: This is the return value of the function. For sys_read(), this is either -1 (error) or the number of bytes successfully read. - @: This is a map similar to the previous lesson, but without any keys ([]) this time, and the name "bytes" which decorates the output. - hist(): This is a map function which summarizes the argument as a power-of-2 histogram. The output shows rows that begin with interval notation, where, for example `[128, 256)` means that the value is: 128 <= value < 256. The next number is the count of occurrences, and then an ASCII histogram is printed to visualize that count. The histogram can be used to study multi-modal distributions. - Other map functions include lhist() (linear hist), count(), sum(), avg(), min(), and max(). # Lesson 6. Kernel Dynamic Tracing of read() Bytes ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200); }' Attaching 1 probe... ^C @bytes: (...,0] 0 | | [0, 200) 66 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [200, 400) 2 |@ | [400, 600) 3 |@@ | [600, 800) 0 | | [800, 1000) 5 |@@@ | [1000, 1200) 0 | | [1200, 1400) 0 | | [1400, 1600) 0 | | [1600, 1800) 0 | | [1800, 2000) 0 | | [2000,...) 39 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` Summarize read() bytes as a linear histogram, and traced using kernel dynamic tracing. - It begins with the probe `kretprobe:vfs_read`: this is the kretprobe probe type (kernel dynamic tracing of function returns) instrumenting the `vfs_read()` kernel function. There is also the kprobe probe type (shown in the next lesson), to instrument when functions begin execution (are entered). These are powerful probe types, letting you trace tens of thousands of different kernel functions. However, these are "unstable" probe types: since they can trace any kernel function, there is no guarantee that your kprobe/kretprobe will work between kernel versions, as the function names, arguments, return values, and roles may change. Also, since it is tracing the raw kernel, you'll need to browse the kernel source to understand what these probes, arguments, and return values, mean. - lhist(): this is a linear histogram, where the arguments are: value, min, max, step. The first argument (`retval`) of vfs_read() is the return value: the number of bytes read. # Lesson 7. Timing read()s ``` # bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start[tid]); }' Attaching 2 probes... [...] @ns[snmp-pass]: [0, 1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 27 |@@@@@@@@@ | [512, 1k) 125 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1k, 2k) 22 |@@@@@@@ | [2k, 4k) 1 | | [4k, 8k) 10 |@@@ | [8k, 16k) 1 | | [16k, 32k) 3 |@ | [32k, 64k) 144 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64k, 128k) 7 |@@ | [128k, 256k) 28 |@@@@@@@@@@ | [256k, 512k) 2 | | [512k, 1M) 3 |@ | [1M, 2M) 1 | | ``` Summarize the time spent in read(), in nanoseconds, as a histogram, by process name. - @start[tid]: This uses the thread ID as a key. There may be many reads in-flight, and we want to store a start timestamp to each. How? We could construct a unique identifier for each read, and use that as the key. But because kernel threads can only be executing one syscall at a time, we can use the thread ID as the unique identifier, as each thread cannot be executing more than one. - nsecs: Nanoseconds since boot. This is a high resolution timestamp counter than can be used to time events. - /@start[tid]/: This filter checks that the start time was seen and recorded. Without this filter, this program may be launched during a read and only catch the end, resulting in a time calculation of now - zero, instead of now - start. - delete(@start[tid]): this frees the variable. # Lesson 8. Count Process-Level Events ``` # bpftrace -e 'tracepoint:sched:sched* { @[probe] = count(); } interval:s:5 { exit(); }' Attaching 25 probes... @[tracepoint:sched:sched_wakeup_new]: 1 @[tracepoint:sched:sched_process_fork]: 1 @[tracepoint:sched:sched_process_exec]: 1 @[tracepoint:sched:sched_process_exit]: 1 @[tracepoint:sched:sched_process_free]: 2 @[tracepoint:sched:sched_process_wait]: 7 @[tracepoint:sched:sched_wake_idle_without_ipi]: 53 @[tracepoint:sched:sched_stat_runtime]: 212 @[tracepoint:sched:sched_wakeup]: 253 @[tracepoint:sched:sched_waking]: 253 @[tracepoint:sched:sched_switch]: 510 ``` Count process-level events for five seconds, printing a summary. - sched: The `sched` probe category has high-level scheduler and process events, such as fork, exec, and context switch. - probe: The full name of the probe. - interval:s:5: This is a probe that fires once every 5 seconds, on one CPU only. It is used for creating script-level intervals or timeouts. - exit(): This exits bpftrace. # Lesson 9. Profile On-CPU Kernel Stacks ``` # bpftrace -e 'profile:hz:99 { @[kstack] = count(); }' Attaching 1 probe... ^C [...] @[ filemap_map_pages+181 __handle_mm_fault+2905 handle_mm_fault+250 __do_page_fault+599 async_page_fault+69 ]: 12 [...] @[ cpuidle_enter_state+164 do_idle+390 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 22122 ``` Profile kernel stacks at 99 Hertz, printing a frequency count. - profile:hz:99: This fires on all CPUs at 99 Hertz. Why 99 and not 100 or 1000? We want frequent enough to catch both the big and small picture of execution, but not too frequent as to perturb performance. 100 Hertz is enough. But we don't want 100 exactly, as sampling may occur in lockstep with other timed activities, hence 99. - kstack: Returns the kernel stack trace. This is used as a key for the map, so that it can be frequency counted. The output of this is ideal to be visualized as a flame graph. There is also `ustack` for the user-level stack trace. # Lesson 10. Scheduler Tracing ``` # bpftrace -e 'tracepoint:sched:sched_switch { @[kstack] = count(); }' ^C [...] @[ __schedule+697 __schedule+697 schedule+50 schedule_timeout+365 xfsaild+274 kthread+248 ret_from_fork+53 ]: 73 @[ __schedule+697 __schedule+697 schedule_idle+40 do_idle+356 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 305 ``` This counts stack traces that led to context switching (off-CPU) events. The above output has been truncated to show the last two only. - sched: The sched category has tracepoints for different kernel CPU scheduler events: sched_switch, sched_wakeup, sched_migrate_task, etc. - sched_switch: This probe fires when a thread leaves CPU. This will be a blocking event: eg, waiting on I/O, a timer, paging/swapping, or a lock. - kstack: A kernel stack trace. - sched_switch fires in thread context, so that the stack refers to the thread who is leaving. As you use other probe types, pay attention to context, as comm, pid, kstack, etc, may not refer to the target of the probe. # Lesson 11. Block I/O Tracing ``` # bpftrace -e 'tracepoint:block:block_rq_issue { @ = hist(args->bytes); }' Attaching 1 probe... ^C @: [0, 1] 1 |@@ | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 24 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 2 |@@@@ | [16K, 32K) 6 |@@@@@@@@@@@@@ | [32K, 64K) 5 |@@@@@@@@@@ | [64K, 128K) 0 | | [128K, 256K) 1 |@@ | ``` Block I/O requests by size in bytes, as a histogram. - tracepoint:block: The block category of tracepoints traces various block I/O (storage) events. - block_rq_issue: This fires when an I/O is issued to the device. - args->bytes: This is a member from the tracepoint block_rq_issue arguments which shows the size in bytes. The context of this probe is important: this fires when the I/O is issued to the device. This often happens in process context, where builtins like comm will show you the process name, but it can also happen from kernel context (eg, readahead) when the pid and comm will not show the application you expect. # Lesson 12. Kernel Struct Tracing ``` # cat path.bt #include #include kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` This uses kernel dynamic tracing of the vfs_open() function, which has a (struct path *) as the first argument. - kprobe: As mentioned earlier, this is the kernel dynamic tracing probe type, which traces the entry of kernel functions (use kretprobe to trace their returns). - `arg0` is a builtin variable containing the first probe argument, the meaning of which is defined by the probe type. For `kprobe`, it is the first argument to the function. Other arguments can be accessed as arg1, ..., argN. - `((struct path *)arg0)->dentry->d_name.name`: this casts `arg0` as `struct path *`, then dereferences dentry, etc. - #include: these were necessary to include struct definitions for path and dentry. 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 (BPF Type Format) 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 [Reference Guide](reference_guide.md) for more capabilities. bpftrace-0.14.0/docs/tutorial_one_liners_chinese.md000066400000000000000000000372221413460502400224170ustar00rootroot00000000000000# bpftrace一行教程 该教程通过12个简å•å°èŠ‚å¸®åŠ©ä½ äº†è§£bpftrace的使用。æ¯ä¸€å°èŠ‚éƒ½æ˜¯ä¸€è¡Œçš„å‘½ä»¤ï¼Œä½ å¯ä»¥ç«‹é©¬è¿è¡Œå¹¶çœ‹åˆ°è¿è¡Œæ•ˆæžœã€‚该教程系列用æ¥ä»‹ç»bpftrace的概念。关于bpftrace的完整å‚考,è§[bpftrace手册](man/adoc/bpftrace.adoc)。 该教程贡献者是Brendan Gregg, Netflix (2018), 基于他的DTrace教程系列 [DTrace Tutorial](https://wiki.freebsd.org/DTrace/Tutorial)。 # 1. 列出所有探针 ``` bpftrace -l 'tracepoint:syscalls:sys_enter_*' ``` "bpftrace -l" 列出所有探测点,并且å¯ä»¥æ·»åŠ æœç´¢é¡¹ã€‚ - 探针是用于æ•获事件数æ®çš„æ£€æµ‹ç‚¹ã€‚ - æä¾›çš„æœç´¢è¯æ”¯æŒé€šé…符如*/? - "bpftrace -l" 也å¯ä»¥é€šè¿‡ç®¡é“传递给grepï¼Œè¿›è¡Œå®Œæ•´çš„æ­£åˆ™è¡¨è¾¾å¼æœç´¢ã€‚ # 2. Hello World ``` # bpftrace -e 'BEGIN { printf("hello world\n"); }' Attaching 1 probe... hello world ^C ``` æ‰“å°æ¬¢è¿Žæ¶ˆæ¯ã€‚ è¿è¡ŒåŽ, 按Ctrl-C结æŸã€‚ - `BEGIN`是一个特殊的探针,在程åºå¼€å§‹çš„æ‰§è¡Œè§¦å‘探针执行,你å¯ä»¥ä½¿ç”¨å®ƒè®¾ç½®å˜é‡å’Œæ‰“å°æ¶ˆæ¯å¤´ã€‚ - 探针å¯ä»¥å…³è”动作,把动作放到{}中。 # 3. 文件打开 ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }' Attaching 1 probe... snmp-pass /proc/cpuinfo snmp-pass /proc/stat snmpd /proc/net/dev snmpd /proc/net/if_inet6 ^C ``` 这里我们跟踪文件打开的时候打å°è¿›ç¨‹å和文件å。 - 该命令开始是`tracepoint:syscalls:sys_enter_openat`: 这个是tracepoint探针类型(å†…æ ¸é™æ€è·Ÿè¸ª),当进入`openat()`系统调用时执行该探针。相比kprobes探针(内核动æ€è·Ÿè¸ªï¼Œåœ¨ç¬¬6节介ç»),我们更加喜欢用tracepoints探针,因为tracepoints有稳定的应用程åºç¼–程接å£ã€‚注æ„:现代linux系统(glibc >= 2.26),`open`总是调用`openat`系统调用。 - `comm`是内建å˜é‡ï¼Œä»£è¡¨å½“å‰è¿›ç¨‹çš„å字。其它类似的å˜é‡è¿˜æœ‰pidå’Œtid,分别表示进程标识和线程标识。 - `args`是一个指针,指å‘该tracepointçš„å‚æ•°ã€‚这个结构时由bpftraceæ ¹æ®tracepointä¿¡æ¯è‡ªåŠ¨ç”Ÿæˆçš„。这个结构的æˆå‘˜å¯ä»¥é€šè¿‡å‘½ä»¤`bpftrace -vl tracepoint:syscalls:sys_enter_openat`找到。 - `args->filename`用æ¥èŽ·å–argsçš„æˆå‘˜å˜é‡`filename`的值。 - `str()`ç”¨æ¥æŠŠå­—ç¬¦ä¸²æŒ‡é’ˆè½¬æ¢æˆå­—符串。 # 4. 进程的系统调用记数统计 ``` bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' Attaching 1 probe... ^C @[bpftrace]: 6 @[systemd]: 24 @[snmp-pass]: 96 @[sshd]: 125 ``` 按Ctrl-CåŽæ‰“å°è¿›ç¨‹çš„系统调用计数。 - @: 表示一ç§ç‰¹æ®Šçš„å˜é‡ç±»åž‹ï¼Œç§°ä¸ºmap,å¯ä»¥ä»¥ä¸åŒçš„æ–¹å¼æ¥å­˜å‚¨å’Œæè¿°æ•°æ®ã€‚ä½ å¯ä»¥åœ¨@åŽæ·»åŠ å¯é€‰çš„å˜é‡å,增加å¯è¯»æ€§æˆ–由多个map时用æ¥åŒºåˆ†ä¸åŒçš„map。 - []: å¯é€‰çš„中括å·å…许设置map的关键字,比较åƒå…³è”数组 - count(): 这个时一个map函数 - 记录被调用次数。因为调用次数ä¿å­˜åœ¨commçš„map里,所以结果是进程执行系统调用的计数统计。 在bpftrace结æŸ(如按Ctrl-C)æ—¶Maps自动打å°å‡ºæ¥ # 5. read()分布统计 ``` # bpftrace -e 'tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args->ret); }' Attaching 1 probe... ^C @bytes: [0, 1] 12 |@@@@@@@@@@@@@@@@@@@@ | [2, 4) 18 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 30 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 19 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [128, 256) 1 |@ ``` 这里跟踪进程å·ä¸º18644的进程执行内核函数sys_read()的统计,打å°å‡ºç›´æ–¹å›¾ã€‚ - /.../: 这里设置一个过滤æ¡ä»¶(æ¡ä»¶åˆ¤æ–­),满足该过滤æ¡ä»¶æ—¶æ‰æ‰§è¡Œ{}里é¢çš„动作。在这个例å­ä¸­æ„æ€æ—¶åªè¿½è¸ªè¿›ç¨‹å·ä¸º18644的进程。过滤æ¡ä»¶è¡¨è¾¾å¼ä¹Ÿæ”¯æŒå¸ƒå°”è¿ç®—如("&&", "||")。 - ret: 表示函数的返回值。对于sys_read(),-1表示错误,其它则表示æˆåŠŸè¯»å–的字节数。 - @: 类似于上节的map,但是这里没有[],使用"bytes"修饰输出。 - hist(): 一个mapå‡½æ•°ï¼Œç”¨æ¥æè¿°ç›´æ–¹å›¾çš„å‚æ•°ã€‚输出行以2次方的间隔开始,如`[128, 256)`表示值大于等于128且å°äºŽ256。åŽé¢è·Ÿç€ä½äºŽè¯¥åŒºé—´çš„ä¸ªæ•°ç»Ÿè®¡ï¼Œæœ€åŽæ˜¯asciiç è¡¨ç¤ºçš„直方图。该图å¯ä»¥ç”¨æ¥ç ”究它的模å¼åˆ†å¸ƒã€‚ - 其它的map函数还有lhist(线性直方图),count(),sum(),avg(),min()å’Œmax()。 # 6. 内核动æ€è·Ÿè¸ªread()的字节数 ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200); }' Attaching 1 probe... ^C @bytes: (...,0] 0 | | [0, 200) 66 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [200, 400) 2 |@ | [400, 600) 3 |@@ | [600, 800) 0 | | [800, 1000) 5 |@@@ | [1000, 1200) 0 | | [1200, 1400) 0 | | [1400, 1600) 0 | | [1600, 1800) 0 | | [1800, 2000) 0 | | [2000,...) 39 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` 使用内核动æ€è·Ÿè¸ªæŠ€æœ¯æ˜¾ç¤ºread()返回字节数的直方图。 - `kretprobe:vfs_read`: 这里是kretprobe类型(动æ€è·Ÿè¸ªå†…核函数返回值)的跟踪`vfs_read`系统调用探针。此外还有kprobe类型的探针用于跟踪内核函数调用。它们是功能强大的探针类型,让我们å¯ä»¥è·Ÿè¸ªæˆåƒä¸Šä¸‡çš„内核函数。然而它们是"ä¸ç¨³å®š"的探针类型:因为它们å¯ä»¥è·Ÿè¸ªä»»æ„内核函数,ä¸ä¿è¯kprobeå’Œkretprobe能够正常工作。由于内核版本的ä¸åŒï¼Œå†…核函数åï¼Œå‚æ•°ï¼Œè¿”回值等å¯èƒ½ä¼šå˜åŒ–。由于它们用æ¥è·Ÿè¸ªåº•å±‚å†…æ ¸çš„ï¼Œä½ éœ€è¦æµè§ˆå†…æ ¸æºä»£ç ï¼Œç†è§£è¿™äº›æŽ¢é’ˆçš„傿•°ã€è¿”回值的æ„义。 - lhist(): 线性直方图函数:傿•°æ˜¯å€¼ï¼Œæœ€å°ï¼Œæœ€å¤§ï¼Œæ­¥è¿›å€¼ã€‚ç¬¬ä¸€ä¸ªå‚æ•°(`retval`)表示系统调用sys_read()返回值:峿ˆåŠŸè¯»å–的字节数。 # 7. read()调用的时间 ``` # bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start[tid]); }' Attaching 2 probes... [...] @ns[snmp-pass]: [0, 1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 27 |@@@@@@@@@ | [512, 1k) 125 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1k, 2k) 22 |@@@@@@@ | [2k, 4k) 1 | | [4k, 8k) 10 |@@@ | [8k, 16k) 1 | | [16k, 32k) 3 |@ | [32k, 64k) 144 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64k, 128k) 7 |@@ | [128k, 256k) 28 |@@@@@@@@@@ | [256k, 512k) 2 | | [512k, 1M) 3 |@ | [1M, 2M) 1 | | ``` æ ¹æ®è¿›ç¨‹åï¼Œä»¥ç›´æ–¹å›¾çš„å½¢å¼æ˜¾ç¤ºread()调用花费的时间,时间å•ä½ä¸ºçº³ç§’。 - @start[tid]: 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: 自系统å¯åŠ¨åˆ°çŽ°åœ¨çš„çº³ç§’æ•°ã€‚è¿™ä¸ªæ˜¯ä¸€ä¸ªé«˜ç²¾åº¦æ—¶é—´æˆ³è¿½è¸ªå¯ä»¥ç”¨è¿½è¸ªæ—¶é—´äº‹ä»¶ã€‚ - /@start[tid]/: 该过滤æ¡ä»¶æ£€æŸ¥èµ·å§‹æ—¶é—´ã€‚如果没有这个过滤æ¡ä»¶ï¼Œåˆ™åªèƒ½æŽ¢æµ‹æ¯æ¬¡read()è°ƒç”¨çš„ç»“æŸæ—¶é—´ï¼Œè€Œä¸æ˜¯now-start。 - delete(@start[tid]): 释放å˜é‡ã€‚. # 8. 统计进程级别的事件 ``` # bpftrace -e 'tracepoint:sched:sched* { @[probe] = count(); } interval:s:5 { exit(); }' Attaching 25 probes... @[tracepoint:sched:sched_wakeup_new]: 1 @[tracepoint:sched:sched_process_fork]: 1 @[tracepoint:sched:sched_process_exec]: 1 @[tracepoint:sched:sched_process_exit]: 1 @[tracepoint:sched:sched_process_free]: 2 @[tracepoint:sched:sched_process_wait]: 7 @[tracepoint:sched:sched_wake_idle_without_ipi]: 53 @[tracepoint:sched:sched_stat_runtime]: 212 @[tracepoint:sched:sched_wakeup]: 253 @[tracepoint:sched:sched_waking]: 253 @[tracepoint:sched:sched_switch]: 510 ``` 这里统计5秒内进程级的事件并打å°ã€‚ - sched: `sched`探针å¯ä»¥æŽ¢æµ‹è°ƒåº¦å™¨çš„高级事件和进程事件如fork, execå’Œä¸Šä¸‹æ–‡åˆ‡æ¢ - probe: 探针的完整åç§° - interval:s:5: 探针æ¯5秒仅在一个cpu上触å‘一次。用æ¥åˆ›å»ºè„šæœ¬çº§åˆ«çš„é—´éš”æˆ–è¶…æ—¶æ—¶é—´ã€‚ - exit(): 退出bpftrace。 # 9. 分æžå†…核实时函数栈 ``` # bpftrace -e 'profile:hz:99 { @[kstack] = count(); }' Attaching 1 probe... ^C [...] @[ filemap_map_pages+181 __handle_mm_fault+2905 handle_mm_fault+250 __do_page_fault+599 async_page_fault+69 ]: 12 [...] @[ cpuidle_enter_state+164 do_idle+390 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 22122 ``` 以99赫兹的频率分æžå†…æ ¸è°ƒç”¨æ ˆå¹¶æ‰“å°æ¬¡æ•°ç»Ÿè®¡ã€‚ - profile:hz:99: 这里所有cpu都以99赫兹的频率采样分æžå†…核栈。为什么是99è€Œä¸æ˜¯100或者1000ï¼Ÿæˆ‘ä»¬æƒ³è¦æŠ“å–足够详细的内核执行时内核栈信æ¯ï¼Œä½†æ˜¯é¢‘çŽ‡å¤ªå¤§å½±å“æ€§èƒ½ã€‚100èµ«å…¹è¶³å¤Ÿäº†ï¼Œä½†æ˜¯æˆ‘ä»¬ä¸æƒ³ç”¨æ­£å¥½100赫兹,因为采样å¯èƒ½å‘生在lockstep中,所以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,pidmkstack等信æ¯ï¼Œä½†æ˜¯å¯èƒ½ä¸èƒ½å¼•用切æ¢åˆ°åˆ‡çº¿çš„上下文信æ¯ã€‚ # 11. å—级I/O跟踪 ``` # bpftrace -e 'tracepoint:block:block_rq_issue { @ = hist(args->bytes); }' Attaching 1 probe... ^C @: [0, 1] 1 |@@ | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 24 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 2 |@@@@ | [16K, 32K) 6 |@@@@@@@@@@@@@ | [32K, 64K) 5 |@@@@@@@@@@ | [64K, 128K) 0 | | [128K, 256K) 1 |@@ | ``` 以上是å—I/O请求字节数的直方图。 - tracepoint:block: å—类别的跟踪点跟踪å—级I/O事件。 - block_rq_issue: 当I/Oæäº¤åˆ°å—设备时触å‘。 - args->bytes: 跟踪点block_rq_issueçš„å‚æ•°æˆå‘˜bytes,表示æäº¤I/O请求的字节数。 探针的上下文是éžå¸¸é‡è¦çš„: å³å½“I/Oæäº¤ç»™å—è®¾å¤‡åŽæ‰€å¤„的上下文。通常ä½äºŽè¿›ç¨‹ä¸Šä¸‹æ–‡ï¼Œé€šè¿‡å†…核的commå¯ä»¥å¾—到进程å,但是也å¯èƒ½æ˜¯å†…核上下文,(如readahead),这时ä¸èƒ½æ˜¾ç¤ºé¢„期的进程å·å’Œè¿›ç¨‹åä¿¡æ¯ã€‚ # 12. 内核结构跟踪 ``` # cat path.bt #include #include kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` 这里使用内核动æ€è·Ÿè¸ªæŠ€æœ¯è·Ÿè¸ªvfs_read()函数,该函数的(struct path *)ä½œä¸ºç¬¬ä¸€ä¸ªå‚æ•°ã€‚ - kprobe: 如å‰é¢æ‰€è¿°ï¼Œè¿™æ˜¯å†…核动æ€è·Ÿè¸ªkprobe探针类型,跟踪内核函数的调用(kretprobe探针类型跟踪内核函数返回值)。 - `arg0` 是一个内建å˜é‡ï¼Œè¡¨ç¤ºæŽ¢é’ˆçš„ç¬¬ä¸€ä¸ªå‚æ•°ï¼Œå…¶å«ä¹‰ç”±æŽ¢é’ˆç±»åž‹å†³å®šã€‚对于`kprobe`ç±»åž‹æŽ¢é’ˆï¼Œå®ƒè¡¨ç¤ºå‡½æ•°çš„ç¬¬ä¸€ä¸ªå‚æ•°ã€‚å…¶å®ƒå‚æ•°ä½¿ç”¨arg1,...,argN访问。 - `((struct path *)arg0)->dentry->d_name.name`: 这里`arg0`作为`struct path *`并引用dentry。 - #include: 包å«å¿…è¦çš„pathå’Œdentry类型声明的头文件。 bfptrace对内核结构跟踪的支æŒå’Œbcc是一样的,å…许使用头文件。这æ„味ç€å¤§å¤šæ•°ç»“构是å¯ç”¨çš„ï¼Œä½†æ˜¯å¹¶ä¸æ˜¯æ‰€æœ‰çš„ï¼Œæœ‰æ—¶éœ€è¦æ‰‹åŠ¨å¢žåŠ æŸäº›ç»“构的声明。例如这个例å­ï¼Œè§[dcsnoop tool](../tools/dcsnoop.bt),包å«struct nameidata的声明。倘若内核有æä¾›BPF (BPF Type Format)æ•°æ®ï¼Œåˆ™æ‰€æœ‰ç»“构都å¯ç”¨ã€‚ 到这里你已ç»ç†è§£äº†bpftrace的大部分功能,你å¯ä»¥å¼€å§‹ä½¿ç”¨å’Œç¼–写强大的一行命令。查阅[å‚考手册](reference_guide.md)更多的功能。 bpftrace-0.14.0/docs/tutorial_one_liners_japanese.md000066400000000000000000000525631413460502400225740ustar00rootroot00000000000000# bpftrace ワンライナーãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ« 12個ã®ç°¡å˜ãªãƒ¬ãƒƒã‚¹ãƒ³ã§ Linux ã® bpftrace ã‚’å­¦ã³ã¾ã—ょã†ï¼Žå„レッスンã¯ãƒ¯ãƒ³ãƒ©ã‚¤ãƒŠãƒ¼ã§ã™ï¼Žã™ãã«è©¦ã™ã“ã¨ãŒã§ã,一連ã®ãƒ¯ãƒ³ãƒ©ã‚¤ãƒŠãƒ¼ã§ bpftrace ã®è¦ç‚¹ãŒåˆ†ã‹ã‚Šã¾ã™ï¼Žbpftrace ã®è©³ç´°ã¯[リファレンスガイド](reference_guide.md)ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„. - 執筆:Brendan Gregg, Netflix (2018).FreeBSD [DTrace Tutorial](https://wiki.freebsd.org/DTrace/Tutorial)(Brendan Gregg 著)ã«åŸºã¥ã. - 原文:[The bpftrace One-Liner Tutorial](https://github.com/iovisor/bpftrace/blob/master/docs/tutorial_one_liners.md) # レッスン 1. プローブã®è¡¨ç¤º ``` bpftrace -l 'tracepoint:syscalls:sys_enter_*' ``` "bpftrace -l" ã¯å…¨ã¦ã®ãƒ—ローブを表示ã—ã¾ã™ï¼Žå¾Œã‚ã«æ¤œç´¢èªžã‚’付ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Ž - プローブã¯ã‚¤ãƒ™ãƒ³ãƒˆãƒ‡ãƒ¼ã‚¿ã‚’æ•æ‰ã™ã‚‹ãŸã‚ã®è¨ˆè£…点ã§ã™ï¼Ž - 検索語ã«ã¯ãƒ¯ã‚¤ãƒ«ãƒ‰ã‚«ãƒ¼ãƒ‰ï¼ã‚°ãƒ­ãƒ–(`*`åŠã³`?`)ãŒä½¿ç”¨ã§ãã¾ã™ï¼Ž - å®Œå…¨ãªæ­£è¦è¡¨ç¾ã‚’利用ã—ãŸã„å ´åˆã¯ "bpftrace -l" ã®å‡ºåŠ›ã‚’ grep(1) ã«ãƒ‘イプã§ãã¾ã™ï¼Ž # レッスン 2. Hello World ``` # bpftrace -e 'BEGIN { printf("hello world\n"); }' Attaching 1 probe... hello world ^C ``` "hello world" を表示ã—ã¾ã™ï¼ŽCtrl-Cã§å®Ÿè¡Œã‚’終了ã—ã¾ã™ï¼Ž - `BEGIN`ã¯ç‰¹åˆ¥ãªãƒ—ローブã§ï¼Œãƒ—ログラムã®é–‹å§‹æ™‚ã«ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼ˆawk ã® BEGIN ã¨åŒæ§˜ã§ã™ï¼‰ï¼Žå¤‰æ•°ã®åˆæœŸåŒ–やヘッダã®å‡ºåŠ›ã«åˆ©ç”¨ã§ãã¾ã™ï¼Ž - { } ã®ä¸­ã§ãƒ—ローブã«é–¢é€£ä»˜ã‘るアクションを定義ã—ã¾ã™ï¼Žã“ã®ä¾‹ã§ã¯ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ãŸã¨ãã« printf() を実行ã—ã¾ã™ï¼Ž # レッスン 3. ファイルã®ã‚ªãƒ¼ãƒ—ン ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }' Attaching 1 probe... snmp-pass /proc/cpuinfo snmp-pass /proc/stat snmpd /proc/net/dev snmpd /proc/net/if_inet6 ^C ``` ファイルã®ã‚ªãƒ¼ãƒ—ンã®ç™ºç”Ÿã‚’トレースã—,ãã®ã¨ãã®ãƒ—ロセスåã¨ãƒ•ァイルã®ãƒ‘スåを表示ã—ã¾ã™ï¼Ž - `tracepoint:syscalls:sys_enter_openat` 㯠tracepoint プローブ(カーãƒãƒ«ã®é™çš„トレーシング)ã§ã™ï¼Žã“れã«ã‚ˆã‚Š `openat()` システムコール呼ã³å‡ºã—時ã«ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼ŽTracepoint ã® API ã¯å®‰å®šæ€§ãŒä¿è¨¼ã•れã¦ã„ã‚‹ãŸã‚,kprobe(カーãƒãƒ«ã®å‹•的トレーシング,レッスン6ã§ç´¹ä»‹ï¼‰ã‚ˆã‚Šã‚‚åˆ©ç”¨ãŒæŽ¨å¥¨ã•れã¾ã™ï¼ŽãªãŠï¼Œæœ€è¿‘ã® Linux(カーãƒãƒ«2.26以上)ã§ã¯ `open` 関数ã¯å¸¸ã« `openat` システムコールを呼ã³ã¾ã™ï¼Ž - `comm` ã¯ãƒ“ルトイン変数ã®ä¸€ã¤ã§ï¼Œç¾åœ¨ã®ãƒ—ロセスåã‚’ä¿æŒã—ã¾ã™ï¼ŽåŒæ§˜ã®ãƒ“ルトイン変数㫠pid ã‚„ tid ãŒã‚りã¾ã™ï¼Ž - `args` ã¯å¯¾è±¡ã® tracepoint ã®å…¨ã¦ã®å¼•æ•°ã‚’å«ã‚€æ§‹é€ ä½“ã¸ã®ãƒã‚¤ãƒ³ã‚¿ã§ã™ï¼Žã“ã®æ§‹é€ ä½“㯠tracepoint ã®æƒ…å ±ã«åŸºã¥ã„㦠bpftrace ãŒè‡ªå‹•ã§ç”Ÿæˆã—ã¾ã™ï¼Žæ§‹é€ ä½“ã®ãƒ¡ãƒ³ãƒã®æƒ…報㯠`bpftrace -vl tracepoint:syscalls:sys_enter_openat` ã§èª¿ã¹ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Ž - `args->filename` 㯠`args` 構造体をå‚ç…§ã—ã¦ãƒ¡ãƒ³ãƒå¤‰æ•° `filename` ã®å€¤ã‚’å–å¾—ã—ã¾ã™ï¼Ž - `str()` ã¯ãƒã‚¤ãƒ³ã‚¿ã‚’文字列ã«å¤‰æ›ã—ã¾ã™ï¼Žï¼ˆè¨³æ³¨ï¼šbpftrace ã¯ãƒã‚¤ãƒ³ã‚¿ã¨æ–‡å­—列を別々ã®ã‚‚ã®ã¨ã—ã¦æ‰±ã„ã¾ã™ï¼Žprintf("%s") ã®å¼•æ•°ã«ã¯æ–‡å­—列を与ãˆã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Ž ) # レッスン 4. プロセスã”ã¨ã®ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«å‘¼ã³å‡ºã—回数 ``` bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' Attaching 1 probe... ^C @[bpftrace]: 6 @[systemd]: 24 @[snmp-pass]: 96 @[sshd]: 125 ``` プロセスã”ã¨ã«ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«å‘¼ã³å‡ºã—ã®å›žæ•°ã‚’計数ã—ã¾ã™ï¼Žé›†è¨ˆçµæžœã¯ Ctrl-C ã§ãƒ—ログラムを終了ã—ãŸéš›ã«è¡¨ç¤ºã•れã¾ã™ï¼Ž - @: ã“れã¯ãƒžãƒƒãƒ—ã¨å‘¼ã°ã‚Œã‚‹ç‰¹æ®Šãªå¤‰æ•°ã§ã™ï¼Žãƒžãƒƒãƒ—ã¯ã•ã¾ã–ã¾ãªæ–¹æ³•ã§ãƒ‡ãƒ¼ã‚¿ã®æ ¼ç´ã‚„集計ãŒã§ãã¾ã™ï¼Ž@ ã®å¾Œã‚ã«å¤‰æ•°å(例ãˆã° "@num")を付ã‘ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ï¼Žã“れã«ã‚ˆã‚Šå¯èª­æ€§ã‚„識別性をå‘上ã§ãã¾ã™ï¼Ž - []: マップã®å¾Œã‚ã«å¤§æ‹¬å¼§ã‚’ã¤ã‘ã‚‹ã¨ï¼Œé€£æƒ³é…列ã®ã‚ˆã†ã«ã‚­ãƒ¼ãŒæŒ‡å®šã§ãã¾ã™ï¼Žã“れã¯çœç•¥å¯èƒ½ã§ã™ï¼Ž - count(): ã“れã¯ãƒžãƒƒãƒ—ã«å¯¾ã™ã‚‹é–¢æ•°ã®ä¸€ã¤ã§ï¼Œå‘¼ã³å‡ºã•れãŸå›žæ•°ã‚’計数ã—ã¾ã™ï¼Žä»Šå›žã®å ´åˆãƒžãƒƒãƒ—ã«ã¯ comm をキーã¨ã—㦠count() ã®å€¤ãŒä¿å­˜ã•れã¾ã™ï¼Žçµæžœã¨ã—ã¦ãƒ—ロセスã”ã¨ã®ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«å‘¼ã³å‡ºã—ã®å›žæ•°ãŒè¨ˆæ•°ã•れã¾ã™ï¼Ž ãƒžãƒƒãƒ—ã«æ ¼ç´ã•れãŸå€¤ã¯ bpftrace ãŒçµ‚了ã—ãŸã¨ã(Ctrl-Cを押ã—ãŸã¨ããªã©ï¼‰ã«è‡ªå‹•ã§è¡¨ç¤ºã•れã¾ã™ï¼Ž # レッスン 5. read() ã®ãƒã‚¤ãƒˆæ•°ã®åˆ†å¸ƒ ``` # bpftrace -e 'tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args->ret); }' Attaching 1 probe... ^C @bytes: [0, 1] 12 |@@@@@@@@@@@@@@@@@@@@ | [2, 4) 18 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 30 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 19 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [128, 256) 1 |@ ``` PID 18644 ã®ãƒ—ロセスã«ã‚ˆã‚‹ã‚«ãƒ¼ãƒãƒ«é–¢æ•° sys_read() ã®æˆ»ã‚Šå€¤ã‚’ヒストグラムã«ã¾ã¨ã‚ã¾ã™ï¼Ž - /.../: ã“れã¯ãƒ•ィルタ(述語ã¨ã‚‚ã„ã†ï¼‰ã§ã™ï¼Žã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯ã“ã®ãƒ•ィルタå¼ãŒçœŸã®ã¨ãã®ã¿å®Ÿè¡Œã•れã¾ã™ï¼Žä»Šå›žã®å ´åˆã¯PIDãŒ18644ã®ã¨ãã«ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒå®Ÿè¡Œã•れã¾ã™ï¼Žãƒ•ィルタ内ã§ã¯ãƒ–ール値ã®ã‚ªãƒšãƒ¬ãƒ¼ã‚¿ãƒ¼ï¼ˆ"&&", "||")ãŒåˆ©ç”¨ã§ãã¾ã™ï¼Ž - ret: é–¢æ•°ã®æˆ»ã‚Šå€¤ã‚’æ„味ã—ã¾ã™ï¼Ž sys_read() ã®å ´åˆï¼Œæˆ»ã‚Šå€¤ã¯ -1 (エラー)ã‹ï¼Œread ã«æˆåŠŸã—ãŸãƒã‚¤ãƒˆæ•°ã§ã™ï¼Ž - @: å‰ã®ãƒ¬ãƒƒã‚¹ãƒ³ã¨åŒæ§˜ã«ãƒžãƒƒãƒ—ã§ã™ï¼Žä»Šå›žã¯ã‚­ãƒ¼ï¼ˆå¤§æ‹¬å¼§ï¼‰ãŒç„¡ã,"bytes" ã¨ã„ã†åå‰ãŒã¤ã„ã¦ã„ã¾ã™ï¼Žã“ã®åå‰ã¯æœ€å¾Œã®çµæžœã®å‡ºåŠ›ã®ã¨ãã«è¡¨ç¤ºã•れã¾ã™ï¼Ž - hist(): 底2ã®å¯¾æ•°ã‚¹ã‚±ãƒ¼ãƒ«ã®ãƒ’ストグラムã¨ã—ã¦å€¤ã‚’集計ã™ã‚‹ãƒžãƒƒãƒ—関数ã§ã™ï¼Žå‡ºåŠ›ã®è¡Œã®å…ˆé ­ã¯å€¤ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒ«ã‚’示ã—ã¾ã™ï¼Žä¾‹ãˆã°ï¼Œ`[128, 256)` ã¯å€¤ãŒ128以上256未満ã§ã‚ã‚‹ã“ã¨ã‚’æ„味ã—ã¾ã™ï¼Žãã®æ¨ªã¯ç™ºç”Ÿå›žæ•°åŠã³ï¼ŒASCII文字ã«ã‚ˆã‚‹ç™ºç”Ÿå›žæ•°ã®ãƒ’ストグラムã§ã™ï¼Žãƒ’ストグラムã¯ã®åˆ†å¸ƒã®å¤šå³°æ€§ã®èª¿æŸ»ã«æ´»ç”¨ã§ãã¾ã™ï¼Ž - ä»–ã®ãƒžãƒƒãƒ—関数ã¨ã—㦠lhist()(線形スケールã®ãƒ’ストグラム),sum(),avg(),min() ãã—㦠max() ãŒã‚りã¾ã™ï¼Ž # レッスン 6. カーãƒãƒ«å‹•的トレーシングã«ã‚ˆã‚‹ read() ã®ãƒã‚¤ãƒˆæ•°ã®é›†è¨ˆ ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200); }' Attaching 1 probe... ^C @bytes: (...,0] 0 | | [0, 200) 66 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [200, 400) 2 |@ | [400, 600) 3 |@@ | [600, 800) 0 | | [800, 1000) 5 |@@@ | [1000, 1200) 0 | | [1200, 1400) 0 | | [1400, 1600) 0 | | [1600, 1800) 0 | | [1800, 2000) 0 | | [2000,...) 39 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` read() ã®ãƒã‚¤ãƒˆæ•°ã‚’線形スケールã®ãƒ’ストグラムã¨ã—ã¦é›†è¨ˆã—ã¾ã™ï¼Žãƒˆãƒ¬ãƒ¼ã‚¹ã«ã¯ã‚«ãƒ¼ãƒãƒ«ã®å‹•的トレーシングを利用ã—ã¾ã™ï¼Ž - `kretprobe:vfs_read` 㯠kretprobe ãƒ—ãƒ­ãƒ¼ãƒ–ï¼ˆé–¢æ•°ã®æˆ»ã‚Šã«å¯¾ã™ã‚‹ã‚«ãƒ¼ãƒãƒ«å‹•的トレーシング)を `vfs_read()` カーãƒãƒ«é–¢æ•°ã«è¨­å®šã—ã¾ã™ï¼Žé–¢æ•°ã®å®Ÿè¡Œé–‹å§‹æ™‚ã«ã‚¤ãƒ™ãƒ³ãƒˆã‚’発生ã•ã›ã‚‹ kprobe プローブ(次ã®ãƒ¬ãƒƒã‚¹ãƒ³ã§ç´¹ä»‹ï¼‰ã‚‚ã‚りã¾ã™ï¼Žã“れらã¯å¼·åŠ›ãªãƒ—ローブタイプã§ï¼Œæ•°ä¸‡ã®ç•°ãªã‚‹ã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã‚’トレースã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Žã—ã‹ã—ã“れらã¯ã€Œä¸å®‰å®šã€ãªãƒ—ローブã§ã™ï¼Žãªãœãªã‚‰ï¼Œã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã®å称,引数,戻り値,ãã—ã¦å½¹å‰²ã¯ã‚«ãƒ¼ãƒãƒ«ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã”ã¨ã«å¤‰ã‚ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ãŸã‚ã§ã™ï¼Žkprobe/kretprobe ãŒç•°ãªã‚‹ã‚«ãƒ¼ãƒãƒ«ã§å‹•作ã™ã‚‹ä¿è¨¼ã¯ã‚りã¾ã›ã‚“.ã¾ãŸï¼Œç”Ÿã®ã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã‚’トレースã™ã‚‹ã“ã¨ã«ãªã‚‹ãŸã‚ï¼Œãƒ—ãƒ­ãƒ¼ãƒ–ã‚„å¼•æ•°ï¼Œæˆ»ã‚Šå€¤ã®æ„味をç†è§£ã™ã‚‹ãŸã‚ã«ã¯ã‚«ãƒ¼ãƒãƒ«ã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’å‚ç…§ã™ã‚‹å¿…è¦ãŒã‚ã‚‹ã§ã—ょã†ï¼Ž - lhist(): 線形スケールã®ãƒ’ストグラムを作æˆã—ã¾ã™ï¼Žå¼•数㯠値,最å°å€¤ï¼Œæœ€å¤§å€¤ï¼Œã‚¹ãƒ†ãƒƒãƒ— ã§ã™ï¼Žæœ€åˆã®å¼•数(`retval`)㯠vfs_read() ã®æˆ»ã‚Šå€¤ã§ï¼Œã“れã¯èª­ã¿å‡ºã—ãŸãƒã‚¤ãƒˆæ•°ã§ã™ï¼Ž # レッスン 7. read() ã®å®Ÿè¡Œæ™‚é–“ã®æ¸¬å®š ``` # bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start[tid]); }' Attaching 2 probes... [...] @ns[snmp-pass]: [0, 1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 27 |@@@@@@@@@ | [512, 1k) 125 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1k, 2k) 22 |@@@@@@@ | [2k, 4k) 1 | | [4k, 8k) 10 |@@@ | [8k, 16k) 1 | | [16k, 32k) 3 |@ | [32k, 64k) 144 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64k, 128k) 7 |@@ | [128k, 256k) 28 |@@@@@@@@@@ | [256k, 512k) 2 | | [512k, 1M) 3 |@ | [1M, 2M) 1 | | ``` read() ã®å®Ÿè¡Œæ™‚間をナノ秒å˜ä½ã§è¨ˆæ¸¬ã—,プロセスã”ã¨ã«ãƒ’ストグラムã§é›†è¨ˆã—ã¾ã™ï¼Ž - @start[tid]: ã“れã¯ãƒžãƒƒãƒ—ã®ã‚­ãƒ¼ã«ã‚¹ãƒ¬ãƒƒãƒ‰IDを利用ã—ã¦ã„ã¾ã™ï¼Žread ã¯åŒæ™‚ã«è¤‡æ•°å®Ÿè¡Œã•れるå¯èƒ½æ€§ãŒã‚りã¾ã™ï¼Žãれãžã‚Œã®é–‹å§‹æ™‚刻をã©ã†ä¿å­˜ã™ã‚Œã°è‰¯ã„ã§ã—ょã†ã‹ï¼Ÿãれãžã‚Œã® read ã«å¯¾ã—ã¦ä¸€æ„ã®è­˜åˆ¥å­ãŒç”Ÿæˆã§ãれã°ï¼Œãれをキーã¨ã—ã¦åˆ©ç”¨ã§ãã¾ã™ï¼Žã‚るカーãƒãƒ«ã‚¹ãƒ¬ãƒƒãƒ‰ã¯ä¸€åº¦ã«ä¸€ã¤ã®ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«ã—ã‹å®Ÿè¡Œã§ããªã„ãŸã‚,スレッドIDを一æ„ã®è­˜åˆ¥å­ã¨ã—ã¦åˆ©ç”¨ã§ãã¾ã™ï¼Ž - nsecs: マシン起動ã‹ã‚‰ã®ãƒŠãƒŽç§’ã‚’æ„味ã—ã¾ã™ï¼Žã“れã¯é«˜ç²¾åº¦ã®ã‚¿ã‚¤ãƒ ã‚¹ã‚¿ãƒ³ãƒ—カウンターã®å€¤ã§ï¼Œã‚¤ãƒ™ãƒ³ãƒˆæ™‚åˆ»ã®æ¸¬å®šã«åˆ©ç”¨ã§ãã¾ã™ï¼Ž - /@start[tid]/: ã“ã®ãƒ•ィルタã¯é–‹å§‹æ™‚é–“ãŒè¨˜éŒ²ã•れã¦ã„ã‚‹ã‹ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¾ã™ï¼Žã“ã®ãƒ•ィルタãŒç„¡ã„å ´åˆï¼Œã“ã®ãƒ—ログラムã¯ã‚ã‚‹ read ã®é–‹å§‹å¾Œã«å®Ÿè¡Œã•れ,ãã® read ã®çµ‚了ã®ã‚¤ãƒ™ãƒ³ãƒˆã®ã¿ã‚’æ•æ‰ã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ï¼Žã“ã®å ´åˆï¼Œçµæžœã¨ã—㦠ç¾åœ¨æ™‚刻 - 開始時間ã§ã¯ãªã,ç¾åœ¨æ™‚刻 - 0 を計算ã™ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ï¼Žï¼ˆè¨³æ³¨ï¼šå­˜åœ¨ã—ãªã„キーã«å¯¾ã™ã‚‹ãƒžãƒƒãƒ—アクセスã¯0ã‚’è¿”ã—ã¾ã™ï¼‰ - delete(@start[tid]]): 変数を解放ã—ã¾ã™ï¼Žï¼ˆè¨³æ³¨ï¼šdelete ã‚’ã—ãŸãƒžãƒƒãƒ—ã®å€¤ã¯ï¼Œãƒ—ログラム終了時ã«è¡¨ç¤ºã•れã¾ã›ã‚“.) # レッスン 8. プロセスレベルã®ã‚¤ãƒ™ãƒ³ãƒˆã®è¨ˆæ•° ``` # bpftrace -e 'tracepoint:sched:sched* { @[probe] = count(); } interval:s:5 { exit(); }' Attaching 25 probes... @[tracepoint:sched:sched_wakeup_new]: 1 @[tracepoint:sched:sched_process_fork]: 1 @[tracepoint:sched:sched_process_exec]: 1 @[tracepoint:sched:sched_process_exit]: 1 @[tracepoint:sched:sched_process_free]: 2 @[tracepoint:sched:sched_process_wait]: 7 @[tracepoint:sched:sched_wake_idle_without_ipi]: 53 @[tracepoint:sched:sched_stat_runtime]: 212 @[tracepoint:sched:sched_wakeup]: 253 @[tracepoint:sched:sched_waking]: 253 @[tracepoint:sched:sched_switch]: 510 ``` 5秒間プロセスレベルã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’計数ã—,サマリを出力ã—ã¾ã™ï¼Ž - sched: tracepoint ã® `sched` プローブカテゴリã«ã¯ï¼Œfork ã‚„ exec,コンテキストスイッãƒãªã©ã®é«˜ä½ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ©ã¨ãƒ—ロセスã«é–¢ã™ã‚‹ã‚¤ãƒ™ãƒ³ãƒˆãŒã‚りã¾ã™ï¼Ž - probe: ãƒ—ãƒ­ãƒ¼ãƒ–ã®æ­£å¼åã‚’ä¿æŒã™ã‚‹ãƒ“ルトイン変数ã§ã™ï¼Žï¼ˆè¨³æ³¨ï¼š`tracepoint:sched:sched*` ã¯ãƒžãƒƒãƒã—ãŸå…¨ã¦ã®ãƒ—ローブã«ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’ç´ä»˜ã‘ã¾ã™ï¼Žprobe を利用ã—ã¦ï¼Œå®Ÿéš›ã®ãƒ—ローブã®å称をプログラム内ã‹ã‚‰å‚ç…§ã§ãã¾ã™ï¼Žï¼‰ - interval:s:5: ã‚る一ã¤ã®CPU上ã§5ç§’é–“ã«ä¸€åº¦å®Ÿè¡Œã•れるプローブã§ã™ï¼Žã‚¹ã‚¯ãƒªãƒ—トã«ã‚ˆã‚‹ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒ«ã‚ã‚‹ã„ã¯ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚¤ãƒ™ãƒ³ãƒˆã®ä½œæˆã«åˆ©ç”¨ã§ãã¾ã™ï¼Ž - exit(): bpftrace を終了ã—ã¾ã™ï¼Ž # レッスン 9. CPU上ã®ã‚«ãƒ¼ãƒãƒ«ã‚¹ã‚¿ãƒƒã‚¯ã®ãƒ—ロファイリング ``` # bpftrace -e 'profile:hz:99 { @[kstack] = count(); }' Attaching 1 probe... ^C [...] @[ filemap_map_pages+181 __handle_mm_fault+2905 handle_mm_fault+250 __do_page_fault+599 async_page_fault+69 ]: 12 [...] @[ cpuidle_enter_state+164 do_idle+390 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 22122 ``` 99ヘルツã§ã‚«ãƒ¼ãƒãƒ«ã‚¹ã‚¿ãƒƒã‚¯ã®ãƒ—ロファイリングをãŠã“ãªã„,ãã®å‡ºç¾é »åº¦ã‚’出力ã—ã¾ã™ï¼Ž - profile:hz:99: å…¨ã¦ã®CPU上ã§99ヘルツã§ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼Žä½•æ•…100ã‚„1000ã§ã¯ãªã99ã§ã—ょã†ã‹ï¼Ÿãƒ—ロファイル頻度ã¯å®Ÿè¡Œã‚’俯瞰的ã«ã‚‚局所的ã«ã‚‚æ‰ãˆã‚‹ã“ã¨ãŒã§ãã‚‹ã»ã©å分ã‹ã¤ï¼Œãƒ‘フォーマンスを乱ã•ãªã„程度ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Ž100ヘルツã¯å分ãªé »åº¦ã§ã™ãŒï¼Œ100ã®å ´åˆä»–ã®ã‚¿ã‚¤ãƒžã«ã‚ˆã‚‹ãƒ­ãƒƒã‚¯é–“éš”ã¨åŒã˜é »åº¦ã§ã‚µãƒ³ãƒ—リングã•れるå¯èƒ½æ€§ãŒã‚りã¾ã™ï¼Žãã“ã§99を利用ã—ã¾ã™ï¼Ž - kstack: カーãƒãƒ«ã®ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ã‚’è¿”ã—ã¾ã™ï¼Žã“れã¯ãƒžãƒƒãƒ—ã®ã‚­ãƒ¼ã¨ã—ã¦åˆ©ç”¨å¯èƒ½ã§ï¼Œcount() ã¨åˆã‚ã›ã¦é »åº¦ã®è¨ˆæ•°ãŒã§ãã¾ã™ï¼Žã“ã®å‡ºåŠ›ã¯ flame graph ã¨ã—ã¦å¯è¦–化ã™ã‚‹ã®ã«æœ€é©ã§ã™ï¼Žã¾ãŸï¼Œãƒ¦ãƒ¼ã‚¶ãƒ¬ãƒ™ãƒ«ã®ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ç”¨ã« `ustack` ãŒã‚りã¾ã™ï¼Ž # Lesson 10. スケジューラã®ãƒˆãƒ¬ãƒ¼ã‚·ãƒ³ã‚° ``` # bpftrace -e 'tracepoint:sched:sched_switch { @[kstack] = count(); }' ^C [...] @[ __schedule+697 __schedule+697 schedule+50 schedule_timeout+365 xfsaild+274 kthread+248 ret_from_fork+53 ]: 73 @[ __schedule+697 __schedule+697 schedule_idle+40 do_idle+356 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 305 ``` コンテキストスイッãƒï¼ˆoff-CPU)イベントã«ç¹‹ãŒã‚‹ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ã‚’計数ã—ã¾ã™ï¼Žä¸Šè¨˜ã¯å‡ºåŠ›ã®æœ€å¾Œã®äºŒã¤ã®ã¿ã‚’表示ã—ã¦ã„ã¾ã™ï¼Ž - sched: tracepoint ã® sched カテゴリã«ã¯ã‚«ãƒ¼ãƒãƒ«ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ©ã‚¤ãƒ™ãƒ³ãƒˆã«é–¢ã™ã‚‹ tracepoint ãŒè¤‡æ•°ã‚りã¾ã™ï¼Žsched_switch, sched_wakeup, sched_migrate_task ãªã©ï¼Ž - sched_switch: ã“ã®ãƒ—ローブã¯ã‚¹ãƒ¬ãƒƒãƒ‰ãŒ CPU ã‹ã‚‰é›¢ã‚Œã‚‹æ™‚ã«ç™ºç”Ÿã—ã¾ã™ï¼Žã“れ㯠I/O å¾…ã¡ã‚„タイマ,ページング/スワッピング,ロックãªã©ã®ãƒ–ロッキングイベントã®ã¨ãã«èµ·ã“りã¾ã™ï¼Ž - kstack: カーãƒãƒ«ã®ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ã§ã™ï¼Ž - sched_switch ã¯ã‚¹ãƒ¬ãƒƒãƒ‰ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆå†…ã§ç™ºç”Ÿã—ã¾ã™ï¼Žãã®ãŸã‚スタックã¯CPUã‹ã‚‰é›¢ã‚Œã‚‹ã‚¹ãƒ¬ãƒƒãƒ‰ã®ã‚‚ã®ã§ã™ï¼Žä»–ã®ãƒ—ローブタイプを利用ã™ã‚‹ã¨ãã¯ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã«æ³¨æ„を払ã†å¿…è¦ãŒã‚りã¾ã™ï¼Žcomm ã‚„ pid, kstack ãªã©ã¯ãƒ—ローブã®å¯¾è±¡ã‚’å‚ç…§ã—ã¦ã„ãªã„å ´åˆãŒã‚りã¾ã™ï¼Ž # Lesson 11. ブロック I/O ã®ãƒˆãƒ¬ãƒ¼ã‚·ãƒ³ã‚° ``` # bpftrace -e 'tracepoint:block:block_rq_issue { @ = hist(args->bytes); }' Attaching 1 probe... ^C @: [0, 1] 1 |@@ | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 24 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 2 |@@@@ | [16K, 32K) 6 |@@@@@@@@@@@@@ | [32K, 64K) 5 |@@@@@@@@@@ | [64K, 128K) 0 | | [128K, 256K) 1 |@@ | ``` ブロックI/Oè¦æ±‚ã‚’ãƒã‚¤ãƒˆå˜ä½ã§ãƒ’ストグラムã¨ã—ã¦è¡¨ç¤ºã—ã¾ã™ï¼Ž - tracepoint:block: tracepoint ã® block ã‚«ãƒ†ã‚´ãƒªã¯æ§˜ã€…ãªãƒ–ロックI/O(ストレージ)イベントをトレースã—ã¾ã™ï¼Ž - block_rq_issue: デãƒã‚¤ã‚¹ã« I/O ãŒç™ºè¡Œã•ã‚ŒãŸæ™‚ã«ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼Ž - args->bytes: Tracepoint ã® block_rq_issue ã®å¼•æ•°ã®ãƒ¡ãƒ³ãƒå¤‰æ•°ã§ï¼Œãƒ–ロックI/Oã®ã‚µã‚¤ã‚ºã‚’ãƒã‚¤ãƒˆå˜ä½ã§è¡¨ã—ã¾ã™ï¼Ž ã“ã®ãƒ—ローブã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã¯é‡è¦ã§ã™ï¼Žã“ã®ã‚¤ãƒ™ãƒ³ãƒˆã¯ãƒ‡ãƒã‚¤ã‚¹ã«å¯¾ã—㦠I/O ãŒç™ºè¡Œã•れãŸã¨ãã«ç™ºç”Ÿã—ã¾ã™ï¼Žã“れã¯ã‚ˆãプロセスコンテキストã§ç™ºç”Ÿã—,ãã®å ´åˆãƒ“ルトイン変数,例ãˆã° comm ã¯ãã®ã¨ãã®ãƒ—ロセスåã‚’æ„味ã—ã¾ã™ãŒï¼Œã“ã®ã‚¤ãƒ™ãƒ³ãƒˆã¯ã‚«ãƒ¼ãƒãƒ«ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆï¼ˆä¾‹ãˆã° readahead)ã§ã‚‚発生ã—ã¾ã™ï¼Žã“ã®å ´åˆ pid ã‚„ comm ã¯äºˆæœŸã—ãªã„ã‚‚ã®ã«ãªã‚‹ã§ã—ょã†ï¼Ž # Lesson 12. カーãƒãƒ«æ§‹é€ ä½“ã®ãƒˆãƒ¬ãƒ¼ã‚·ãƒ³ã‚° ``` # cat path.bt #include #include kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` カーãƒãƒ«ã®å‹•的トレーシング㧠vfs_open() をトレーシングã—ã¾ã™ï¼Žã“ã®é–¢æ•°ã¯ (struct path *) を第一引数ã«å–りã¾ã™ï¼Ž - kprobe: 以å‰èª¬æ˜Žã—ãŸã‚ˆã†ã«ï¼Œã“れã¯ã‚«ãƒ¼ãƒãƒ«ã®å‹•的トレーシングをãŠã“ãªã†ãƒ—ローブタイプã§ï¼Œã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã®é–‹å§‹ã‚’トレースã—ã¾ã™ï¼ˆé–¢æ•°ã‹ã‚‰ã®æˆ»ã‚Šã®ãƒˆãƒ¬ãƒ¼ã‚¹ã«ã¯ kretprobe を利用ã—ã¾ã™ï¼‰ï¼Ž - `arg0` ã¯ãƒ“ルトイン変数ã§ï¼Œãƒ—ãƒ­ãƒ¼ãƒ–ã®æœ€åˆã®å¼•æ•°ã‚’æ„味ã—ã¾ã™ï¼Žã“れã¯ãƒ—ローブタイプã”ã¨ã«æ„味ãŒç•°ãªã‚Šï¼Œ`kprobe` ã®å ´åˆã¯é–¢æ•°ã®æœ€åˆã®å¼•æ•°ã‚’æ„味ã—ã¾ã™ï¼Žä»–ã®å¼•æ•°ã«ã¯ arg1, ..., argN ã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ï¼Ž - `((struct path *)arg0)->dentry->d_name.name`: `arg0` ã‚’ `struct path *` ã«ã‚­ãƒ£ã‚¹ãƒˆã—ã¦ã‹ã‚‰ dentry や後続ã®ãƒ¡ãƒ³ãƒå¤‰æ•°ã‚’å‚ç…§ã—ã¾ã™ï¼Ž - #include: path 㨠dentry ã®æ§‹é€ ä½“定義ã®ãŸã‚ã«å¿…è¦ãªãƒ•ァイルをインクルードã—ã¾ã™ï¼Ž カーãƒãƒ«æ§‹é€ ä½“ã®ã‚µãƒãƒ¼ãƒˆã¯ bcc ã¨åŒæ§˜ã«ã‚«ãƒ¼ãƒãƒ«ãƒ˜ãƒƒãƒ€ã‚’利用ã—ã¾ã™ï¼Žã—ãŸãŒã£ã¦å¤šãã®æ§‹é€ ä½“ãŒåˆ©ç”¨å¯èƒ½ã§ã™ãŒï¼Œå…¨ã¦ã§ã¯ã‚りã¾ã›ã‚“.場åˆã«ã‚ˆã£ã¦ã¯æ‰‹å‹•ã§æ§‹é€ ä½“を定義ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Žä¾‹ãˆã° [dcsnoop tool](../tools/dcsnoop.bt) ã§ã¯ nameidata 構造体ã®ä¸€éƒ¨ã‚’手動ã§å®šç¾©ã—ã¦ã„ã¾ã™ï¼Žã“れã¯ã“ã®æ§‹é€ ä½“ãŒãƒ˜ãƒƒãƒ€å†…ã§å®šç¾©ã•れã¦ã„ãªã„ãŸã‚ã§ã™ï¼ŽLinuxカーãƒãƒ«ã®BTFデータãŒã‚ã‚‹å ´åˆï¼Œå…¨ã¦ã®æ§‹é€ ä½“ãŒåˆ©ç”¨å¯èƒ½ã§ã™ï¼Ž ã“ã“ã¾ã§ã§ bpftrace ã®å¤šãã‚’ç†è§£ã—,強力ãªãƒ¯ãƒ³ãƒ©ã‚¤ãƒŠãƒ¼ã‚’作æˆãƒ»åˆ©ç”¨ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Žbpftrace ã®ãã®ä»–ã®æ©Ÿèƒ½ã«ã¤ã„ã¦ã¯ [リファレンスガイド](reference_guide.md) ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„. bpftrace-0.14.0/images/000077500000000000000000000000001413460502400146265ustar00rootroot00000000000000bpftrace-0.14.0/images/bpftrace.graffle000066400000000000000000005120061413460502400177500ustar00rootroot00000000000000 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/iovisor/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/iovisor/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.14.0/images/bpftrace_internals_2018.png000066400000000000000000007267131413460502400216730ustar00rootroot00000000000000‰PNG  IHDRÜYóºysRGB®Îé pHYsgŸÒRÕiTXtXML:com.adobe.xmp 5 1 2 ‹O²@IDATxìÝ œUóÿÇñ??[´(K´j“”%¥Õ–¥”¬©$Z´/DQJ‘µ¥…¢ÄŸÈZ*Ú)QiQ)RI¥Âó?ïoΙ{ïÜ;sïm¦¦™×÷ñ˜¹gù~Ïò¼wîÜû9ßóù’⣠€ € € € € €@²»9ä<‡&Ûšv € € € € € *@À=Õ‚)@@@@@’ àž4 @@@@@H àžjÁ € € € € €I pOšŽ† € € € € €¤ pOµ` @@@@@¤¸'MGC@@@@@R¸§Z0… € € € € €@ÒÜ“¦£! € € € € €©ÜS-˜B@@@@@ iîIÓÑ@@@@@Tî©L!€ € € € € ´÷¤éhˆ € € € € €@ª÷T ¦@@@@@HZ€€{Òt4D@@@@@ U€€{ªS € € € € €$-@À=i:"€ € € € € *@À=Õ‚)@@@@@’ àž4 @@@@@H àžjÁ € € € € €I pOšŽ† € € € € €¤ pOµ` @@@@@¤¸'MGC@@@@@R¸§Z0… € € € € €@ÒÜ“¦£! € € € € €©ÜS-˜B@@@@@ iîIÓÑ@@@@@Tî©L!€ € € € € ´÷¤éhˆ € € € € €@ª÷T ¦@@@@@HZ€€{Òt4D@@@@@ U€€{ªS € € € € €$-@À=i:"€ € € € € *@À=Õ‚)@@@@@’ àž4 @@@@@H àžjÁ € € € € €I pOšŽ† € € € € €¤ pOµ` @@@@@¤¸'MGC@@@@@R¸§Z0… € € € € €@ÒÜ“¦£! € € € € €©ÜS-˜B@@@@@ iîIÓÑ@@@@@Tî©L!€ € € € € ´÷¤éhˆ € € € € €@ª÷T ¦@@@@@HZ€€{Òt4D@@@@@ U€€{ªS € € € € €$-@À=i:"€ € € € € *@À=Õ‚)@@@@@’ àž4 @@@@@H àžjÁ € € € € €I pOšŽ† € € € € €¤ pOµ` @@@@@¤¸'MGC@@@@@RKd @Èž{öì±… Z±bÅÜOö<ÊÄŽjóæÍ6uêT[µj•ýóÏ?VªT)kݺub¡ö~رc‡-^¼Ø ,hå˗߯ûfgY/ßg²^= € €D ÐÃ=R„y@ÈvëÖ­³š5kÚØ±c³Ý±%s@=ö˜•,YÒn½õV2dˆ;¯ž={&³)ÚìGE‹¹×aïÞ½÷ã^ÙÕþÈiï3ûËý € €„ p÷`@ÈR)S¦X=¬^½z6cÆ Û¶m›ýüó϶iÓ¦°ýŽ1ÂF¶Œ™Ü-Àk"w?ÿœ= € €ÀÁ!@J™ƒãyâ(@@ ‡(ˆ~ÜqÇÙ¤I“ìè£Îêðæ51lØ0Ó²Ûo¿=l93¹W€×Dî}î9s@@ƒG€îÏsÅ‘"€ €@øòË/íôÓO ¶ç€Óâ@@@<î¼ @@ý(°uëV+T¨Ð~Ü#»B@@Ø_Ü÷—4ûA@@@@ÈÑÜsôÓËÉ!€ € € € €ì/îûKšý € € € € €ähÃrôÙqr € £þþûo{ë­·l„ öÝwßÙÚµkm÷îÝvâ‰'Ú©§žjW]u•]ýõvòÉ'gè Üê#FŒ°š5kZ­Zµ‚úýõ—=ÿüó6gÎ[²d‰}óÍ7vØa‡YáÂ…íÌ3ÏtÛoР~øáA›Ð‰x tÖtÌË—/·Èå?ýô“tÒIAÝ-[¶ØþóŸ4õJ•*e7ÜpCPïÃ?´ùóç[§N,oÞ¼ÁrM|ÿý÷î¸øáûóÏ?íÞ{ï [¿k×.9r¤}öÙg¶fÍ÷³}ûv;å”S¬hÑ¢V¢D »õÖ[í / kÏ̪U«ìå—_¶·ß~ÛÖ­[g›6m²üùó›Ž_϶ٲeK;âˆ#âÙ\X='N´÷ßß6lØ`›7o¶|ùò¹ç¹nݺΧjÕªam²z&Öó ×å“O>éŒe²cÇwþeÊ”±J•*YÇŽíøãzx‘¯‘x_¡Ë,«Xç§}E{Ū¿/¡ç:•¯ãÐýøÓüñ‡{ßÑë{ÅŠöã?Ú/¿üâÞwŠ/n%K–´æÍ›ÛÅ_ì7á@@  ’â•\t¾œ* €„ PŸvÚivß}÷Ùý÷ßïÎ`êÔ©vÇwØúõë]°õ¬³Î²³Ï>Û HªóìÙ³í믿vë^xá»òÊ+Ó=óhû˜>}º d/[¶Ìj×®m+VtvøèýàƒÜþ+W®lS¦L±bÅŠ¥ÙÇ1ǶLmH?òÈ#Ö+°¯@¾_táàC±£Ž:Ê_ä/ºè"{ýõ׃e=zô°Ç{ÌG‘"EÜr;‡ bï¼óŽýóÏ?n™Ý+W®tÓ ú?õÔS6hÐ ?ãŒ3¬B… .®ãÕ… ýÌ;׈uî£G¶Ò¥KûMoBû¾óÎ;Mç¤ òÑEmÛ¶™‚Îß~û­-]ºÔ-ëÙ³§{ÿûßÿ¦·I·N[{÷îm>ø ›×EjÕª¹Àµ¹óæÍs^´²sçÎö裆™ºFûðK¯)]Œ¹úê«íµ×^ ÛRäó ÀzëÖ­mòäÉîbŒ.Μ{çHÚ/¿üÒ½Fu¡@Bºwï¶=ÍD¾vâ}M¨mf[EžŸö‘Þë,²~fxhŸ¡%³_ÇÑÞB÷§i=Ÿz^ýõWwaª~ýúîu\ @÷¼êâƒþnt©J•*îo3™ V‘ûe@@à Øí}‡Ë£ã@@ [ xouHñîî8½€kÊ¡‡šâõbNñ‚ê)^P9êñüñÇ)^oíïCOJß¾}cÖSãÈ}Œ?Þíó²Ë.KñzhGݾðKñí)^°-å„NHÑþ2*^Oø”FeT-Å»Àâõ€Î°ž¨uÇé]xpçç¤Ý¼×Ó6eðàÁ)}ôQŠ×Ë9ÅëÁl«OŸ>®Žw"Å ÊË#'¼^»)^08Å»âåS¼‹‘UÂæ½ jJÓ¦Mݶ½ )^¯ù°õ¡3³fÍJñ‚•®®×8tUÔé;w¦4lØÐÕoÑ¢EÊÆ£Öóz»§x=ç]½5j¤xÁí¨õ’Y¨cÖëÐ ¸§iú<èØ¼{JžrZ)X”ªF¹ËÇWJ¥cQ ¥MéÖ­›K¥¹Ý¬žW÷1cƸT2C‡µX)Z”êE©J^y啸ó§wéÒÅ奞9sfÌÓð‚ò.UŒÒÔ(]Mhzœ˜¼Jû¢-J䨣Võz»üù“&M²6mÚD­¹PÏ¡ŽCùã•âfåõVÞt¥ŸÑë"£¢T?/¾ø¢{ÍŒ5*£ê®Ïj«x_gþf•GV½ŽýãŽ|ô.¶¸çH©Š4FDÌUàFå}6l˜Ë¡ß¸qãÈÕéηjÕʼÞÅîØ4¨åþ( 0å/5j´cPŽo ˜«‹ ûRö‡U<¯³ÐsÈ ¬z‡wä´0¿ä’K"W¥;ï×WÎ~  € €@î àž;žgÎ@!ðÛo¿™—‚Ä4@a¢ÅËÿíz¹{9µ]Pu@@²—÷ìõ|p4 € ŽÀ©§žšÎÚŒW)o¹r†§p/Z´hÆJ§†Ÿ]½Ü÷g)Q¢D¦ïî?þ0å´WUy³£ÿÂB¹r墭ާe+W®tí÷5à^©R%S~s¥lÉê’l€ÙäVw`$Sö—U¢¯³åjÏë8´~´iݵ A{•k_ò^zé¥n^i®( € €„ :Ã4 € J•*µO‡§”/Eбô‚áûpWî÷c=6Ý}ìÓIÄh¬@`¢%%%Å »lÙ2Sà\=ÙþùgÛ¸q£ë®ô&JU¢T/… ²Y³f¥ÙÅþ¸/X°ÀÔƒ;Ù¢ApUÔË]ç‘•%Ùó¾“pÏj«D_gûÃ#3^Çñø×ªU˾þúkëÕ«—1ÂÞ{ï=ÓØU«Vµ .¸À ñ’c"žýS@@àà à~pg:Ÿ§žzÊ¥fz÷ÝwÝX ³gÏ.Féï£|ùò¦à|›6mÜßN¼Û¦ € €@Î àž3žGÎ@\!à‡÷åd7lØ`éõ”ß³gϾl޵ݵkW–÷¤Nö _zé%kÖ¬™}ôÑnðTå¦VÊ•“O>9©Mžxâ‰®ÝæÍ›M¹ª3³hÀI•¥K—ºàfn;§m+·Yeöë8Ñ׃‚êúéÚµ«ýïÿ³Ï?ÿÜæÎksæÌqꯟzõê¹O?ýôDwA}@@ƒT€îéÇa#€ üÞÔÉž»¤ÜºukºÁp¥SÙ—ò÷ß»^ã~.÷}ÙVf·}ñÅ]°])0tñB½Ü•‹:Ù`»ŽÏÏ«ï§mÉÌcö/Œø½·3sÛ9m[¹É*+^ÇûòzPþýóÏ?ߺwïîÆ4ð¥ˆÞ—ãÍ̶ÊÅ®ÁÕ›]¹§MÏk0OÿÂBVÜ}C??yfzä´m嫬zgæëAie®»î:ûæ›o\ïv½§4jÔ(3wÁ¶@@²±÷lüäph € .°}ûvûî»ïÂ&0§”*çw^ÌV ˜kÆd‹ßÛD'»Ìn·xñbûõ×_íúë¯7åkO´ø=t#müóÜ× øý÷ßoÇ|Øà¨~Ù7Mô˜ýúO>ù¤µk×Î ë/Ëi¹Å*«^Çé½–,Yb´D_ãJóÓ±cGkÙ²¥-Z´È”v‰‚ € ó¸çüç˜3D@ G ><éóÑà›* 4ˆ¹ ¥ßÿý˜ë3Z¡HUÎ8㌌ªî×õØQ¥fÍš ïWix.\µrÁ_xá…6yòdÓ‘d‹Rp(|è µE‹µ ØÔ©SÝ]Él[i„î¹ç—ÒãÐCsîGßÜb•U¯ãô^[סOŸ>.MLzõb­óßo>ýôÓXUXŽ € ƒrî·Žô$q* € *0nÜ8Û±cGê‚8§ÔKuþüù.î÷ÊŽÕtäÈ‘±V¥»\)o”_Zùœ«W¯žnÝxV*}Ff «R¸pá„79jÔ(ûý÷ßc¶ëÝ»· ¶«'y2eÅŠ¶lÙ2»êª«Âš+@®¼Øêá.×dŠ.ìܹӥøH¦}vkë5‘[¬²òuë¹®\¹²~øá6sæÌXUÒ]ž?~·~õêÕéÖc% € €@Î àž3žGÎ@\!дiSÛµk—%üñÇQß¾}Óµ:óÌ3]j?°—n刕>ú¨ë‰Ý¯_¿ˆ5‰Ïyä‘n`ÓÄ[FoQ¡B·B)¿ýö›=ýôÓé¬zñÅ[•*UlÈ!îùIdû wêÔÉ”÷úšk®IÓTë ,èRzh@ÚDŠz·<ØŽ8∑C;£×Dn°ÊÊ×q¬×–‚ígu–Í™3Çtq(ÑòÁ¸&§œrJ¢M© € p p?Ÿ4@Ü*P¾|ykÓ¦K2vìØ¸î»ï>SÏx¥S‰Ô ÝPÆ ]ït=þüóÏ¡«ÒV/ð#F¸¶ @ïk9餓\¯ñM›6íë¦\ûŠ+ºÇ7ß|3îíýòË/V¿~}ÓEˆ‹.ºÈµ‹5xªz¹oÙ²Åõ$Oä„xÀ ⪔gŸ}všc;öØc­G.ЩA_cí?MCoAëÖ­méÒ¥6tèЄ‰¶½½,£×Dn°Êê×q¬çXêþøãkܸqB•ÔfÊ”)nÜ„:uêÄÚ<Ë@@r÷ôdr* €äO•®EÁTõ^Ψh0ÎXñâÅãÎÁüØcÙ×_íö“Ñ@‰ê¡­€°.h£GÎèâZ¯€¿Š]ÌŒ¢´º0fÌ›8qb†›Ü¸q£ËÍ®Ad•7ßO‹±|ùò¨m5jdO=õ” žW­Z5ÃÁm•æ¥[·n¦çG9®Ó»+ sçÎîB‰rð_rÉ%¦ é•mÛ¶YÛ¶mmÒ¤Iîu¢ç&'”x^9Ý*«_DZ^'—]v™é¢’.à(è¾fÍšXUƒåºX¦ »z~衇ÜxÁJ&@@r¬÷ûÔrb €äLÿþ÷¿®Çè7Þh½zõrØW^yÅÜJIIq'­žÖo½õ–]zé¥.«L•Öáä“OŽ åÜsϵéÓ§»æÕªUs=ê-ZôlUýÇ4 ªC W0T‹ž~úéqí#£J ëunåÊ•³ž={º @4™¢”-&LpÍš5³+®¸Â>üðCw¾›ÙŸ}ö™uéÒÅÔ“XmæÍ›gÊy¯óTiÞ¼¹õïß?ê……öíÛ»€»î 8ï¼ó\Ð[w",^¼Ø”F=ß•–CMt·‚RÐ蕟=½M:ê(ç  ,³fͲsÎ9Çy(?».¨lÞ¼ÙæÎkJëSºtiw|-[¶´dóÊ»f³_ñ¼&rºÕþxÇzÚuqè¶Ûns7zýêB›òºë½@EGúÒß•.”UªTÉý=é}BwgP@@r‰€÷Á‚ €dko@MEÒS¼Ô0aÇ9mÚ´/˜žâáÜz/ŸâÝ´ê)R$Å §x~†µ‹6m^/Ö/P–âõî¶yÌ1ǤvØaÁ|Ñ¢ES¼ oŠ„¶Ù4˼|Ð)^oð4Ë£-ðÒQ¤xÁü/NŠÚyAé”&Mš„UõuDz~ýú°å±f¼ÀtÊÝwß⥠ÎÁËqž¢™é§L™2)^Þö/z°/`žâõBwëd|à 7ë"'¼½)Þˆ°mjûþó¤}\pÁ) ,ˆlšá¼ÌLñRÏǪm…>çš÷zò§xH2ÜV¢¼`¿ÛïÕW_¦i¢ÏCä¼q ܶg̘¹*l>ž×„ß 3­=¿DëûÇì?f䑯ãhïþñ„>zã ¤\yå•a¯ÁþøcSzÝp '83=/Çw\¼›‰ZoÕªUæ3í§Ÿ~rwèØõœ«wû¾n;êâ…9Ùj¼Žc=õ[·n5¥œÒRÌ}ôѦ<ûº“Fi¯ô7EA@ÈU»½ï†y¸çªçœ“E@XéÜcµa9 € € €ÿ ¸€;9Üy= € € € € € Ü3‘M € € € € € @À× € € € € €™ @À=Ù € € € € €Üy € € € € € Ü3‘M € € € € € pHŠW`@@r»€>íÚµË?üp÷“Û=8@@@„vrÈ!y¸'dFe@@@@@Ò¸€;)eÒ¸°@@@@@ĸ'nF @@@@@ÒpOCÂ@@@@@ àž¸-@@@@@H#@À= @@@@@H\€€{âf´@@@@@@ ÷4$,@@@@@@ qÑ@@@@@4‡ýöÛo¶xñâ4+X€ € € € € € üóÏ?êÜ^ý°Y³fYŸ>},%%%ãVÔ@@@@@@0/à~¸·àéC¼@;‘ö0f@@@@@HH`÷!‡’‡î ™Q@@@@@èÜ£»°@@@@@„¸'ÄEe@@@@@¢ pîÂR@@@@@ àž•@@@@@ˆ.@À=º K@@@@@HH€€{B\TF@@@@@ º÷è.,E@@@@@ !î qQ@@@@@èÜ£»°@@@@@„¸'ÄEe@@@@@¢ pîÂR@@@@@ àž•@@@@@ˆ.@À=º K@@@@@HH€€{B\TF@@@@@ º÷è.,E@@@@@ !î qQ@@@@@èÜ£»°@@@@@„¸'ÄEe@@@@@¢ pîÂR@@@@@ àž•@@@@@ˆ.@À=º K@@@@@HH€€{B\TF@@@@@ º÷è.,E@@@@@ !î qQ@@@@@èÜ£»°@@@@@„¸'ÄEe@@@@@¢ pîÂR@@@@@ àž•@@@@@ˆ.@À=º K@@@@@HH€€{B\TF@@@@@ º÷è.,E@@@@@ !î qQ@@@@@èÜ£»°@@@@@„¸'ÄEe@@@@@¢ pîÂR@@@@@8,¡ÚTF@@  ¬]»ÖvíÚ•ƒÏSCX‡v˜•-[6Öj–#€ €q p‹‰J € €9]`äȑ֮]»œ~šœ¤#бcG6lX:5X… €¤/«îÛ·o·?ÿü3}Ö"€@Ž8òÈ#íØcÍQçÄÉ €dÀ’%K܆>ú(Ëstž¬Ù [El)ðÇŸØŽ;mñâÅÙòø8(ˆO€¸O|NÔB »rÈ!V¨P¡ìr8™v¹&à>tèPëÚ¥‹¥dBƒAà?ÿ9ÔÆÞnºé¦ƒáp9F@l гGë{/=ݳÁSÁ! °ß^ü¾]{}Ûý¶?v„™/ ¸O—®]ŒÀOæÛ²E²R qãÆ6yòä¬ÜÅ~ßv®4uΜ9¼çî÷—;DàÀ üý÷?6wîÜ  € € €@– (îCà'ËxÙ0Y&ðñÇgÙ¶Ô†sMwøÉa÷[ûv7û³<"€@è׈õ@Îüsj € € €@˜Ààaƒ¬U»–a˘Aì'°ü›VãÌÚÙïÀ2áˆrM÷L°b € € € € €Ä à“† € € € € €Ä/@À=~+j"€ € € € € S€€{LV € € € € € ¿÷ø­¨‰ € € € € €@Lî1iX € € € € €@üÜã·¢& € € € € €1‹¹† €¤Û¶m³Þ½{Û–-[Ò3à°´ÀñÇo<ð€(Pà@ ûG@@8ˆ¸DO‡Š ŸÀk¯½f#FŒˆ¯2µˆ!pöÙgÛ­·Þc-‹@@@H+@À=­ K@r?þøÃÁ™gžiÍ›7?ÈφÃßßãÇ·Å‹›ÿ:Úßûg € € €¯÷ƒ÷¹ãÈ@2(Uª”ÝrË-Ôb5á³gÏv÷ð¥Ì!€@f¬X±Ú}õmº›*X0¿U<£œpB¡të±2_àõ×_·_~ùÅêׯoÅ‹Ïô¤¤¤Ø¬Y³lýúõÞßø vÉ%—dú>Ø  €Z€€û~Ø? € €¹DàíiY÷žã:[ÜëÕ­nÇö³ãŽËW*!€À¾ ôë×Ï]tVz¾¬¸ßxãöÒK/¹ƒÌ›7¯mß¾ÝV®\i<òˆåË—Ï~øá};Z#€ ÍÇÀ! € € €a?ÿ¼Å&½ô¦U>çrûüóÅaë˜AƒOàçŸ‚íƒ ²çž{ÎÄO?ýd£F² &|'Å#€ E€îQPX„ € €Y+pćÛ#ƒïN³“íÛwÚ‚O¿²·ÞžîÖýðÃvëí½lÑ·íCISŸ pp¬Y³Æh¡B…ìî»ÓþígÁQ"€ ±=Ü36¢ € €d²Àá‡nîh‘æ§÷=ìÍ7FÛ3#{\²d™½=mF0ÏZ`þüùÖ°aC0`€KÁrÕUWY‘"Eìœsα;î¸Ã¶lÙ’æ?øàkÔ¨‘•,YÒ ,h+V´Þ½{ÛŽ;‚º·Ýv›Ûî¦M›ì‰'ž°2eÊX­ZµÜú?ÿüÓ|ðA«R¥ŠüñV¬X1Ó~5öHd™9s¦5mÚÔJ”(ᎩsçΦmÆ[þþûo—æ¥|ùòvÊ)§Ø•W^i¯¼òJXóD :vìhÝ»wwíwîÜéÎñþûï·öíÛ[¯^½Ür厗©êªÌ˜1ÃÍ2ľûî;»âŠ+œÛ»ï¾ëÖÇãé*z¿Þ~ûmkÒ¤‰zê©.UÎå—_nï½÷ž¿:xÜW·`CL €äjî¹úéçä@@Èž·µ¾>làÔù E=ÐÝ»÷ØoÝ”7?p)hfÍZ`›7oZ7½…«V­µW^fï¼;3MµåËWÛ»ïͲ‰/¾a|ø±­^½.MX öìùÝæÎ]hoNý0©ãRû™³æÛ ^· ~е–ïgüÑÞ|óM—¥N:Þs<צ¿ýö[{úé§]ÛïÑ­C›8q¢ tÊ”)¶{÷n«P¡‚ýðæÔ*—^zipô~ø¡Ûnß¾}­[·n.¿¹Ô*7¶{î¹ÇV­Ze „+(>uêT«W¯^Xðxøðán™ö¥À¾‚ÿÆ ³óÎ;Ͼùæ›`_éMôéÓÇõBÿïÿkGy¤½õÖ[víµ×Ú½÷Þ4KÄà믿¶eË–¹¶ÿûßÿì³Ï>³+VØ’%KÒ,W] ¬*ã>úÈ.¼ðB4WP^âõÔvú÷ïï‚õ6þüÎmÚ´iÎý©§žRW2ÃÍß €¹[€€{î~þ9{@@²­@™2%‚c[·nC0­‰M›6Ûímï¶ÂEªÚù5ÛÕMÚØM7w±:õo°“‹Vsóß}·7…EhÃñÏO¶Cÿ{ªûé}sçovc³ÎVæ´:vÝ ¬ç]ÕÇÕ*TºØÊŸQß\q‹ÝÜ¢›]rY +]®¶]pá56}Æœ nä„‚ô5k_ky T´µšZ£Æ·Û‰'ŸkåN¯gOx!²º›/Yºfpl J*H¯}Õ­£µ¸¥»Í›ÿEÔv,,÷'â1ðë&ú¨¿ç—^zÉ”椓N²¯œ € €µÀk¯¿ëõ‚MÍ9}zùÒÁùÜ×ïq/ Å*7_°`ëug[«Sû|/‡öIöõÒvg¯‡¼ â×^s³=3úEopÖ{‚¶¡oLyßë­»Ú2kÔ¨b•*–·|ùŽõR~ì±Á ª>üÐÝÖøêKìðÃÿk_|¹Ôîíû¸— c™©z»;îµµ«ëº*5M³æ]Ý:õF¾³gkÚ¤(ÏfÍ^àŽM)oF>3Ñòæ=ÆtW°ŸÐ ƒ'Ÿ|¢]Xóûì°#QŽõ¼yóºÜìê^©R%«_¿¾}õÕW6bÄ—BE½Â-Zä]0Ú›.&lÞÌ5×\¶Hۻ뮻졇²xÀhåÊ•³š5kÚõ×_o «(E‹ŠÒ§(ÇzhQ ^eåÊ•iŽ9´ž¦/¹ä’ÈE®g¹z›+Ÿzh‰× ´M"ÓÊ_­Zµ°&ñzúil”[?´èb‡Òùø%³Üüíñˆ »¸çî石G@@à€(˜¼xñ·aûö2nØæ-[mÚ;3mÄÈ Á:õn¿áú«‚y­÷Ë€û»YÛ67ù³V÷„Böܘ‡í̳¸e³f¬‹œP°]öW^~Êê׫¬Vª˜?þØœ,S¦¤õè~[°®H‘ÂvjÉ¢Vïâf. ®àü¢EߨYgUpuzÜ9ÈÛ5ó¬wÍon´-Q¢ˆÕ¬q®qæ%öûïØSO¿`wöhã‚éA¥'tl×6½ÜÆŽìõè=:r5óÙD@ƒ’ªÇtd9æ˜c\À]¢*•L›6mlôèÑvÔQGYõêÕ]ïsõ²Ö¡êaY4 jdQ: ^ªÞÞJÅâç?×vÕSûÉ'Ÿ €Uû -~Ð:_¾|¡‹£NkpÑÈ¢sR äUóñ¨^²%Ò"Ï_ýÕíV¹ìÓ+¡ç´/néíƒu €¹G€€{îy®9S@@²€ՕϹ<ÃãQ/ñ‡êÕ«\l¿Ô«[ÝŸ wîÜ›ÆC ~ýuG°<ÚÄý}»†ÛU§¨T÷‹RÀ<3êEÓ ®:•3Î(g›6~æW ¿ÿþ—ÊÆ¯l÷+zj1»ý¶lØðq^º‘Ý.è~oŸŽþêàQ=ÛŸûˆ =2XÆDöPooõ?üðÃÓÀž~Š˜’%Kºží Š(PÀåDWm¿tîÜÙŸ {ô_kþB¥¦Ù¶m›•.]Ú €ªå &k0T °ª”(÷ÝwŸ•)SÆ>ýôSkÕª•µoßÞoî•zFéU”=£¢ÜçJ!ZüWuN¡%ƒÐú‰NGZèNx=åñù矧É;¯¼÷íÚµó.|ýîr¼g–[¢çF}@œ)°÷cÎ<7Î @@bõ&Ÿ9}’µl‘š^Cƒ^|QÍàG©!öìùÝ¥x™üÚ;^º—Ǭ‘7€jnpV ®:üÉñÞ@”K\à2¨ðïÄ×KS“,Vôd—Ã]yÜ# äOíaüUD/›··¾`»‘ôöàôS©JØ®P¡‚—èä Ø{Ê)§xiRƒíê¡îæý¶±D/Z´¨ëM®ÁRU40j¯^½‚üñ û©`žyæ7¨¿= tªüòj³eËqÌG剭§WŸþyWÿâ‹/kAXƒˆ? ®ÁKã)þ ­ñxú„V,ü¢»´lêÔ©ÎÕ¯·¯nþöyDÈÝôpÏÝÏš³ÿpú'öË/ÛÝrÝîZ¸ð iêìÏêùôùçKlËÖ_ìÈ#°Š^o¢¢Þ—  € €ÀÁ- À¹zG–‚óÛÊy?e½Þº%ì°Ã¢e™9k¾=ûÜÿÙ'^@{íÚ .mGä¶2š/UªXXÏd¿¾R„¼5e¬ Ü+¼Šò®kpU€UåŽ×ñ÷èv[féÒÔÜÖÓÞùÈKó‘¿É˜Û¶íýìY¡|HÎúÈuÌg¥ˆQÐ[ñ³Î:ËæÎ `ªžçz-)Ç»‚Ê_ýµ]qÅV»vmSÐ|üøñÁ‰Œ5ÊŠ/ÌGN(ŒÒ¼¬^½Úåƒoذ¡Ë¯€±îúÐz¥^iÖ¬™Tõ³Ï>³sÏ=×7nl[·nµ7ÞxÛ`·õèÑà ¹ýÈùíÛ·›rÑßpà nPV¨Õs_±6iÒ$¬z<a "fü‹ ÜËGŽ ˆ¨•:›ˆçÍ7ßìzÿË£jÕªÖ´iS—ƒþÍ7ßtÔs¤÷¢ÌrK=J¦@r³@ôO¯¹Y$—Ÿûݽq=vÄ0uÊ»¼pÝ&¢/(-néá}@Lí‰Ð®m3{jxwLÊ•©ÜŸ*+–‹úeÉ­ä € €@¶PgŠ'‡ÝŸÔqÝߨÝ?`hÐ6þ¼®Gz©S‹ÛùÕÎ2Û4m¬5š:&²Žz×¾àM{÷½YöæÔí£™óÜ«~=}F}hðÓÀ«sf¿êEõ?›ªÎ ^.y âšQQê˜h%½c‹VŸeF@©H”‹½gÏž6yòdwêE®^ï ŽzN+Ø­AGõ£´.ݺus”\ž8q¢5oÞÜÕöKAíwÞyÇåjÿðÃÝÀ«ª§»4®»î:{úé§]3ø§OŸnJU3a„ p­¼íýû÷·>}úDÛ|šeJQóðûAZµRÛ­W¯žËï÷H÷Åcà×ö¨ ò:t¨³ùþûïƒãŽV?O]ÄG§Nœ±?Pê±ÇëRÉtíÚÕí"³Ü¢/ËÈo¿1Í»8÷—;”3ά`¥Ë–ŠyXºÛä½·>ÖëoþÒ+/Î1—YÓgÛ¶_öŽýP­FU;©pôÿÑÄ à†ÅV`ûöi‚í‘GT£VS¯—ÆÞÛW.ŸéõøH;°Pdæ@@ g(À 'œàr Çê3Δ³ˆ˜?ÿË Ø~ćÛóã·&/ ò««þ'Ÿ¤Í¯¹ÍG£Õ¹ô’Z¦僟5{M|qн:yš[¶lÙ*{ôñÑ6p@+[65·õEõkØ ãwu’ùu衇$ÓŒ6@à¶ÛnsÁr¥ˆQ`üôÓOO3jëÖ­]oñeË–ywîér±û¹Ôo¼ñF×û¼R¥J¶fÍš˜gP¶lYûàƒÜ « J«g¶z½G‚ª`ò³Ï>k#GŽ´o¿ýֽΤÖ~3*Êîõú^±b…wwÇf—Gb•x jÔ¨óN÷ìÒ¾èøUZ´há~¢í3^OµÕöž{î9wÑCyèu‘¢T©RirÙï‹[´cdÙI C«.öÛÎßÜ!õ¤_Ì€»RS5kÔÒfÏø88üÇF<œ+‚í:á½ÙW »sŸ8åyîÁ«€‰D¸'*Fýý"ð¥×{ÝïÙ®D¯Oi§—/ã}pÝ/»g' € ÍfΜéz+*è~ÓM7YË–-½Ε²ùQsx™! 2~Qn÷¦×„ì¨u¿ÄHÓâ·ËèñwgÚgŸíýÂ]£F«[gïÀ¬êIßðª‹ÜϽ´Gí65oÞî±\ÙSƒMúÙW.w´ þ7ß|góæïm£ô9U«VÚ1qp (x®,é•÷=´Rú½ï=ñè½V§þ nþö¶÷Øw+¿·FW]ì½tíÆ=ÿjl¯Xñ4«_oo[×€_€R¹ÜqÇ ÿ_>hN0ŽÅ $ª p(æÓú†¶öþÛ©yÛ ¶O‡˜­¸gë§çÀÜ‹“¦X·½ô–à`4À•zœ~æAï–¿üÁrâÜj ƒWW­˜åò\vêr¿ëÍã׉µZu¯³?Ϲ©J~/¥A{Ú=ÞÀ®‘¥t¹Ún‘Žg˦½·ç–,]Ó mpËÿؽÜÞyw–µ»£ýøãÏnÙÿ½ô¤]Ó¤›~éå©öØc‚ãv ÿýU¼ø)Ö¹ã-Ö¥s«ÐÅÁ´®ßÓç{ö¹W¼œƒÛƒåšP@¿g÷Û­Íí7Dý‚§X>ô´ >Î Tm ÚæÉs´Õº°ª=3b ÛF°‚ 8`uëÖuk€zsæÌ±^½zyïm©$®_¿¾ô«@aÇ:iÒ$÷Å\ 5@W—.]Ü`iþk ûw½7hð³aƹ v膴]å-Õàbéå­~ýõ×íÞ{ï5Üý¢[½Û·oowß}wØ­è«V­òûÛ{¼ºõ|ýú½½.ì×ÀlÝ»w·Ÿ~úÉmF9P6lè¦uìTMýÈR´hQkÛ¶­µk>ha¨ƒÊϪ¼¯¡9c•“VËxà—ëUçñ /ØÎ;ƒÝüñÖ·o_ד9XÈÿ è³þôCÊ™œ÷²hÔð"+WîT7€éŸþiw÷~Øý„ž©,ݱc§ýþûnÌŸRekÙ¤‰Ãìºk¯­sºŒ×AdøÐ~vGÇû\)o~`ú‰V´Í›nÜû¾¨õµjUµûûvuyæ°ø‘gÜOdÛ%ŠØÛoŽMÓs>²óÙS@ÿ/Ÿ|òÉìypûé¨0ØOÐì,P§–¶7ßaÓ¦¼ì%Ñ`ûúµ?Øâ/[‘¢EìŒÊî<¸fÕ÷^û%–ç˜7îU{íõwÓœ¾¸¼þÆ{^Oî¥6åµQ^ðØ9G<3!Èmº¡D¶Ú.Ùéwß›eMš¶3ý3‰,C‡=g]»ˆ\Ì+h߭ǶaãOöÈà{‚åšøá‡­Éµí½Ÿ©ƒ …Vذá'ëÒ­¿ëͤè?çolÖÙÞ{vh7½Ëû2íì¼ó9ã*UÈI›)_|ñ… 4(T=ØæÏŸïNaëÖÔ Y™yNˆløðái6©A†Þzë-[¼x±½øâ‹®—wšJÞÔ¯¿þz›7o^šÕ˜¬U«V. Ÿf¥·`Û¶möÈ#Øûï¿oãÇw½½#ëÝsÏ=n ´ÈåúªÔÑÞ #ëj~úôén¸hõ5›ö«(hß»woï"çÖ¿ÿ¨Õt1@½•$´(Xª º »^}õÕÐÕnZN;vtwœwÞyiÖgÆ‚wß}× ñ×ÌØÛÈÝñO!åL÷t°aÃÇÛ}ý[ŸÈL»¶Íì¤“Ž·A^爅 —¤iªRoo}ƒë”úOïíÓÑÝMٮýÞÝ9ËÃÚªãÉm­¯·»ïjï¶¶’@ö“€. ·oÙɦ¼:5Øc¼Áv}ŸòÐ05|Œm‰èã—JgU´‡†´sϯâ/ {\¹|¥uiÓÃ>Ÿ¿0ìûO©2§Úí[[«v-Ãê3ƒ@f pÏLͶ-Û>ú({pàVÛë©£/0ÓgÌñzs?êz }ÿýÖòÖžöù‚7cöÌÑ@R‰lãÁîô¹úÕ ô¿gãÆï °Ô®Uͺu½Õé–ñ®bžáåÆTizÝÞ›íŸnzÔÈAî K¬T wözȽÁ*¿æ…5Ïs=ó•.FñÐ`ûÕ.q=”ªU=Ëôþ¿WÞöòwumŸò¬ûò–/_^·OýjwǽA°]½ÙôzàkûkÖü`S¦~`O˜`êy%ËÁ´»{µÚö¼ëÁ Ø®|Ÿô°³¼T2JwóôÈ ¦» Ô¿NýmÍÊÙ^š…ø v’Ë'ü;|õ°¹œ$×¾‚²YQlW:“ûî»ÏjÔ¨aê‘={öl0`€÷þ¸ÃÖ­[çz’ôÑGQßß~ûm/·ïwn]µjÕ\ª˜¼y÷¾·(H­Þó*Ú‡zÑתUËíã“O>qVpõ’íµ×ÂNqÚ´iaÁvõ¿úê«­páÂ.-Œ.>©ç|êGŸ{Õ c㛬`Á.ài^ªÅôÊTñzì½ãÝó›w!v™×ög+áÝ9©vÇ{LÔ¦kV~u9 @ÈL}ŽîÔº«½öÒëÁfã ¶ÿêÅlÚ4k–ïÝ߈â(¼3Ý.:¿MxmœU®}àfûolx³Íûxß4ê£z³ßxUsûdæÞïG¡•Ô+þÚËo´9‹gÚÉ^|'´è"Bgïüt<‘eÕw«í®N÷Ø·K—ÙƒCH÷®áȶÌ#¯÷x¥ra=?sú$/€R)8{ݾ[ÃûòP½æ5î‹Í—_.u½Ý_}iP't"ÑmhP*•eËV›)Rä$»âòzÁ¼ŽA%4ÇyÝ:ÕíÔS‹u"'–/_m×6½ÜÆŽìåI>:X=fìKÁt±b'ÛĆxyƒ–õözG}ñï9êÂ/‡gƒËê¸õ³gôªÒ6ç~üªõò¯«+vŠ»XçÿınÙ$/mp—›ÑRÑ—®ÏæO ö«mÈá˜c޶Q£'¹Ox¹H§$&°eë¶ÄP8ô·­@¬hôK™2eLÁóK.¹Ä{Üíz¹«·û•W^éW lWzܸqV»ví`¹. ù½¹uQë*UR{l(íŒr¨k ìÏœ9Ó‡¿å_TÜ/ t‡Æ‹/î‚÷uêÔñîÐùÁ¯óQÇ©`½ôÊ1ï—çŸÞŸ´"EŠØ¨Q£Âr®+.èüõÞ©óºøâ‹ƒ6¡C† ±k®¹&X¤Þð~À] • ~̘1Ázå‡We›ŠÒáPHT VÊ™D·Cý/ Ôú‰UÊ—/múÙ×¢ô/úI¦(¸®à;@ì  `{·¶=íåöÆ$tLu/®÷©ýîÛO«PÎzèe+ŸaÖo´gGŽ·É“^³MÞêFõ›ØÂ• ¬`¡‚iN{Ú”wM=Ðõ½ªZóìôЧ[Þ(ã÷=?z‚«Ó½w»ªÉ•ö§×éòÿ&¾jƾhJó›wQû±C\z'JQÓ®yoÌ•ÿ¹,{Þáµ½ÂòÈosgϳû{ p½òÇ=ó¼ëÝ5wß Þ~SÈ4C3mKl(Ç \Óä²°`»‚•*•·[[]ëÏÚØgÿ/˜ŽœÈŒmDn3™yõlnì#aÁvmç‚êUìÝiãÝÏûï<½C÷±sç®`ö×_wÓJ—ã—Ýn ‚íþ2=ÞãõhW¯¨F /¶Ò¥Šyì‡=9Î¥”P÷w‹º_ØõÏGå©§_ÚºüŠK d’_ŒãÚ8•rµ€r˜‡Û} jÚ¬Y3Ö&LH}Ÿþ;¡<ê¡Áv- d_wÝuaÁv¿½f½õÖÔÞ¡ÊmñÐÇsŒuêÔÉ_<,XÐõ¾¤3¡žíÊOlWõªU«º º8 ö‘œªNhn{]ˆV *lWå~¶I“´½PCíCsÔGÛËÈH@pô:ÖëYi¡( € €@N1ä›ðì‹a§8ãý™öîÔ÷–E›Q¯òÇíí¸Xæ´ÒöÁüwìÒ+/±SŠžbçU?×F>ÿ¤5¿mï÷!õ.9tt´Í¸`{^/{À+ïL²7g¼îRÃÜÓ¿Wšºº80ðñþÖ«ß^P¾¼ë1?è‰Ö¦ómAÝ¥K¾ ¦5Ñ÷Îþ.Ø®éacž°>ÜmJ?S¬DQ»¾ùµöÎ'oß_ž}ú9Ûö õdEÉ\Ã2wsl-' \Þ nÌӹẫlø“ãÝúïV~³^fl#æÆX¡üšGudšÑz=mܸÉV|·Æ¾ýv¥×ƒý#ûpú'iÚiÁ²å©½*Ï?ÿì¨utËñäWF¤Y·$$—§‚êsæ|ž¦Ž)RØK1³1HásÆå¢Öcat@I¥ÔPš JîPh *ª^çYQbõÖÖ¾ Vo?øífB~©÷ú-·Ü²dïäŠ+‚eÑÍþJ­S.vå8÷Khû .¸À»S&zÊ‚È@¿ß>òQšuÔQ‘‹½Á ˹ŸÐÊÕ®óÕ1(¿¼zßgTÊ–-µŠ‚î6lpëBƒï~e¥¡ñ‹zÐgUQ®| PKÉž·ß~»½òJjϬDR¯#ý-µlÙÒtׇþk _  € “~òƧó‹RïÞ½ÇÍv¹½»Íúò,;ñ¤üÕiG?96èÞ;"U²Ï|´¿¡xŽNi™d׸Ç{l´·|Pù߉ôÖ…Öuœ]ºt ‚íJ¯£ô6Ê÷Zbµ ­Ã4Y) ;"SÆdåþØ6 €@|W\q…UªTÉÛ_‹ÔZ?ýô“KݧÏY•2R½ÛlW@OƱcÇF ¸+˜>xð`P× õ]t‘K‰§Ôz—Gãìè8UGw'†Žó³{÷n7~ˆÎLŽ?þøà$+V¬L3@VÜÖáÖ Ø®í÷´ŸÍš>Û”ŽÅÍß=ÐjÖ©ár¦»ÿþÒ߃:úE¥&û™ÿ”"§ø›ÉÔÇÐã;þ„B¦žö•“"b@Õg=ñpG)—Öyíõ÷ì––ió]‰còëï* 4VÉŒmÄÚö¾.W/„¹ó¾63vôCv≩tü¿D¹«u§•+ܧ¾5ÝÎ>û ¿IðøþÛ¥ Z¸ùãŽËo›6~æ¦ÕƒÞ¸Ï_ðeÌ€ûÄßðpûõi~scSj x©S§ÚM7Ýõ@”bÅ/饅ñë„>†Ü5€c­ZµBWÓZçõ,÷KùòåýIÓ—EÖ£å±×mÏɽw~úiꭣÆ ³NH›çqÛ6JÖ˜vû&@ʘ}ó£5 Õº .;—gŸ}ÖžŸ9r¤MŸ>ÝÔÙ!²7¾z諨7|è9é3¢Ræ¼õÖ[®‡»î矾ûq ¼_º[Ðoï½÷ÆìÈá×çÌ(Z¼hØæ”_ýÉg‡ZÃzMÜE¦?ÿüÓÚÜÜÞ ˆšÞQÁõ’¥KØòoöŽ;5ö¥QV²T‰°mÅ;«Sd¼ícÕ+U6õNÆZõ/´㟌U•åd©y,²”÷àÞøÛÓf˜Æ‘eåÊïmÄÈÔ`ÍÍÍ®Ž¬ÌgÆ6‚EL„æñڱ㷈µÏnß¾3È×®Þ ÑnIZ²d™-Z>⵿å›nlèOÚˆg&ºÁMƒÿN ~xd°èòu‚Üc¡f z28Ž ²71b仹E7»­ÍÝöì¸W¶‡â0ÀPÏ¥3f¤9 åçô¿¤ie¬ÛÓ4üwÁÍ7߬Ò`¡m…ò¥‡î#´Rº¨w•_4䯿†ß©¼ëÊšlQ:šß~Ûûžë½S½Â–,Y’ì.h‡@ ‚(`¡T³g϶V­Z‘Ÿ=aE €@"º°Ý°aCSúÅ‹ÛUW]åÝù[Ä”[ÿ·lÙ¶¹Ûn»ÍÕß´i“»3LÄý ë{öì±ûî»Ï.¼ðB;餓¬Zµj¦»É¶nݶ Í(P«¯.²ëB}Û¶m]=‹~¶oß›AŸS4?dÈw^½Ê ,hï¾»w0Á>øÀ5jdê åêUÝ»wo M;çoã±Ç³9sæ˜]×Evçÿýßÿ¹cÓ£îvÓ]yþí·ßNsÌ‘ :wîìŽM†*¡ûÑû¸>Ûh{:Ç»îº+øÜѾ}{S ´u~;vtóþ¯?þØ®½öZ7¸»z•ëùÜ£žç/¿ü²ÛœöwÙe—¹t1ãÆów<ênD•Ÿþ9XæO<ðÀ¦€ý 7Ü`;wîôóˆ@¶8¿f5k×µMp|˼Ô,ý½žî‘¥tH@{ႽwæFÖÑü«/N¶ Ͼè~ÒKu­í¾, =¾/?[”&Ý“¿m]4ðoá‚ÔŽ˜þzØWîû*˜ÃÛ_qÕ­öÀÀ'í‹/¾v½¹GyÉjÔºÖû€³7€S´èÉxŽÆ‘Ûˆ¶ÝÂ…S{£+Ïú+¯N³×^ßû2ZýÈe äóní+âëÖ¨ÆMÛÙŒæz˜¶xJרÐaÏYú7†5û|áïCì·ìš& ¬fÍsÝ´ÚÔ¬}­½0áu[³f½}õÕ·Ö¶}oûhæ<·^)%ºtjl«QË­v­jn^iiªV¿Ú&{ƒ¶jÚÙ³?µ{û>fÝ{¦þsëÞ5¾ÜhÁ˜@,¸þúëíÑGõþÞ¿r_dÇï¾”ù=»õ¥[ƒ2&Rôûšk®qMÔ³äꫯ¶áÇ»àµícÆŒq¹Óý/Ãúâ{å•W†í¢{÷îÁÄÏ?ÿÜõ˜Ò>}ñS0@A[²EyâýT7zïT^l87oÞl«V­r=Á"éË/¿ ûŸì¾i‡@¨@ž_èN7¥1™2eŠx½B… .?hÐ »ôÒKýݹٵ íCœê3Žg j«÷í·ßnú<¤êùª ´ŽEÒ+3gÎtǦsWñUwðé³ñê\µlÙ2—6¦]»½y¦u1_ËTtŸ}ö™ÉÄ/ú\¦öê´ õJ-¦slܸ±ûüã×KïQÇ ÏYºp¢  ˜«èÜô¹'´T¯^ÝÍÊHt<~.w ˜ªçP? ЪÆtv¸§ÿ]Vîô²Á!Ž~r¬Mï£`^×6Ûû]EÓ µóàs#Ç[»­k›ö⸗ökçA ²ªT7*«¾[íöïfB~ýöÛ.»©Qsw|:F.Š…à0™i‡fÚ–ØPŽhÙâû믿ì¾~[•ªWy£J×·6íîqilþüyí…q§ûæÙôšû¼X°Õ¯¬šöÎGvÝ L9Ù)wßÕ>¨>wîB«q3;é”ó¼2õ¬k÷îÂB±b'u† }Öª]Ð8˜óÌC^Ï‹Òn^yÜ[ÜÒÝJ•­egU¹ÜFžÔ{üÑÞÞé Á¼&ž10hûí·+­éuwØ©e.´Úõ®·ƒž RÉ<п»5¾:õƒoØF˜A" ÞRzÔS}É®ZµªuíÚÕu@ú¬[“I¥múùáÕãM_ÌÕNËî¼óΠçZåʕݗ¸H€óÎ;Ï^|ñEÓ_*ú2«`Àã?î1U°\_>÷¥è\ý¢AÀä¡Ô6çž{®)‡©¾‡õGŒÖóÞoË#û" ¿1åÈ­[·np‘i_¶G[@ +V¸ÿqú«ÿ‰ê®ÿ‡ëÖ­swÞDnSÏu!|ùòå®·yÏž=Ýt5Üÿlmu[½´•¯\=Øý@¯z†k.è«Wµêj\™ô‚E ´qÄ.½‰Ž±^½z.§¸¶Ù£GSà^Î4Wñyóæ™Î)´(ЭÀ±:h àësÐèÑ£Ýg ¥°ÓEw-WQ*•dŠíê±®íéxÔY@套^ ýo¼ñ†[vâ‰'ÚÆÝ-ÐE }ÑÝwê¡®öºãN禀·.‚¼÷Þ{®mz¿ü»o¼ñFWMô™JκÀZt¡W+dW@^ŸÁd¨ÎúåwÂmÃ4ÙY@ïOn‡–š}ºc«Î¶esêÝ6 ^fÔªîNcåò•vIõ6õµ·]þ÷¹³çÛƒ}Û}=ïN³}×¶Áôþ˜ÐÅ¿¦î¿[ÛžÖÿžöùü…öýêµ6iüËvSÛm­7f¡ÊéË[­z{ß»öÇñ±Ü#@À=÷<× Ÿiï»ï°zXä¨ÐúS¥J%›ûñdïCÕyénWyÇ_yù)¯7djÐZ ÙF¬Üß·‹×»¾‘÷ò𠊶›H¹­õõ6|h?ïƒQÁ4Í*U:Íf|ø¢}ùùÛ1D-S¦¤}¾àMkßîfïƒXž4Û¨P¡¬½ÿîón}äJµ]øéTëØ¡EÔ¶gžYÞ&¿2Âîñž d/õæÒ«Ð ²ŽPïAꥦ/t~¯§D¼P¡B®×—¾d† åo§@¦/çê=:Hª¿^굦[Å;tèàõyóæ5èu{º¾Gp꺺ô¦[´házœéX#‹¾xª§—¾àêu Y%èÿü¬:¶‹èÿ²‘ý¸ãŽsÁ]©(Û®]»Â€”úMÁkõžVpK½Íõ~6jÔ(wÁ^•uwì3Ï<ã}ËãàºSL½À•zE0µ÷b <+­M¬¢°¾üòË]ºmSáõ9¡_¿~A3õ(÷Sv*ÐZÔs_½ÙU”ÏY)dTt®ê®ãW[?­.:$S”NG–*Ú¦Ÿ6OÁý 6¤»Iù©W»‚ݺXáëá§yê©§üÅQ•ÎfÖ¬Y®·¾>ë©è3’RרhðÔТãÕE-ת«»&Ožlê•_ÂKi“H:›Ðm3À¨tVEëѧ[°ûÍÞÝü[§v¶ÑŠÇF¼*”ÇO÷u´ùØ3‰»Ú÷ÆÝ®µïÇ1¥zµ²ö4aH‹ãy1[îܹÕ÷H“ ò;kÖ¬Æe]:'• ÃB¸9ñ9ÎA ýB¾õÙCÎ ë…s¼iç•!8’ufƒsÿ¿ÇhhADû™3g¼Œ”‡ãØlp´Ãðÿvóo}_È7·±§ Y0D§ê¦KØá߃¼3%"ø­ çð¡^Çúº~<þ|UÄ|鯛èzD­#øA7ÌyC°AÎZ÷nÀXÐà‡ãŽ|¯‚$ô~¸'G"Щg{Ù±e§@¶sënLLÓß«ãÔiÄõ¯Jã’1ï5‰³eÉ–YsÚw–ŠÕßÙ®÷/\¼ tk×S._ð¢Ò¯á·OÃõ¥cö’ ¡÷ß·ô6Ü“€o Ðáî[b¡°>", Îû]3Ç1HªX˪|W§¦Æ¶"ÔM—},ÂñW[HÀf˰ÀP®lQ[—Œs˜cÍQŽÍ·†¶©S§P›oÛ²> @ÐÀ¿]üˆ6ÿöïÑÀmëÇ£­ûà.¢°ôD©xÕŽwkC„œn¶®ë×|ÚãË*°Ù2,>àG=HÀ>Ý{ŽTIØ]w-õÑá>pð$m1îŠzÎî3f.Urƒ~u¸;Ú|ìyb§N_PÉí3Ðán°Z'¥æœÅ÷kƒ îzÞýºÙ‰¬G¿ëŽj½Ž¾×Ï£ž¾ ­G¶ëu°÷Îm¾êâ»B«V­” úÇyH˜Š·â n+¨­ù¡/="eÿ0¯îcOßÞ±4sôª/0„V= r4æ…½ ¢vñFXA}3¤y°Ç 9ÃÙ , @3íÚµ9  %Û/®Ù}{üÛÞyØûü>aˆ ?XIµ\:I"iÁƒNiœ$¥S ›÷ª×¨Ž`óÉì©­ù§_zÛU¾‚yåÀé=òîí;¹xî’¸=z,ÉS$“´ÒHÔhò›Öì>ºÝúIÀOèp÷66"  Ç#€{ˆ|;xð ~.X°ÀBÚR4º&**5iÒDÕå àE RÅ’Usô&Kš(H^ºlC)]ªõùZÉ9 :ENœ<'ÖÍÒqñæ$X 7§*‚”tÃ[Xº,KªT©ôÓjoŽàÖ½¡÷n9 °žõ ëÃ[uÏž=ÓÞìýOÖ Úç^™ù~¨ƒÈv,È#JûܹsòxÁÙ) Føþƒùá <³éoïé¼Í×ô2¾A¶ND¼!híüß¾}»Òp‡Æ;î¹éÑ£‡jŽD±Öo(¤Ôb°Øa~«P¿÷$’àßJªÔ)Õæ¨ó‚sÎw &:Ü“6ïE$@$@LÑiÐeE„^kÆÀ 2¨¨+ü ×_IÇ0êÖ­+*Ý«žŒ‚Ý“@ˆ&€<;AmO(¬IÈ,X´F“¥¸©†ó÷½‡R»fqëßѯA=_ÞŸlÀ[e'NT‰Íõë/A"Mä5ñN r/ȱ9è°C[]7$ÝDRPè„C¢ÚëØŸ8qBºté¢ÔáLÇÿ×ÍZìz{¯öˆÌ†ÁoÎEù:}‘À«¶Žp^_@€\Ùàô†–:¶¶mÛ Þ0,~L˜0A•QÇ+Ó“¥Bë^ב7×EBT$M…ž>¶lÙ²©\;à<¢ØcĈa4<ÞÝ»wÕ1âÒH€H€BÿÒB×¼9[  ‘„ QXú;D`!Bz꺳Qq£F’É“'‡Hœ 8:«WoIÍÚ­%yª‚’4E~iظ‹µzÏÓ°¡‹^µzK™8iž\¿~[*Ui.qâçí;öKÇ΃Ե£GOkúÀwTuŸ?iÑrÔà|ý†””.ºk9iz÷#ù ÕP÷¯^³•¬X¹É¢w÷Ö+B>¡¿Ò²y=M¯:šŠðíÞµ• 9Û÷í?*µëjγԅ$WÞÊjÞ{hYc{÷1æŽ\:º=xà&˜æ~äÈ)ý´¿ÍÝèÐ<7Œ oàU©ÖR= ̱mûþZ³ç<»vÿ)Õjü*©ÒVŸçìå¤O¿±šdÉ[cÞ=7Da9]rç«"ñæRŸ?Ü÷À¿ŒözÁžÏHËV½Ôð<&Lœ«½¢_\Š–¨«w¡öÈ…yâó޳!®Õ¨õ›´ï0@•—,]g®¢ÊÆOU×/ñ|ÍSå 8¹’ž={* ’aÆ©ä¤ãÆS#0`€§Hië!N:U%@…S¸\¹rê-4,ˆc6vìXålGÿïÆý/^¬¢§‹+¦å}r2¢ßQÇ'ƒ.<œÖøn4úïܹ³-úŸ|&êÎbŸú ìëú":0þ~ýú©!T«VMñÛЬG¢ø^½zi’¡y•F=? óbËðƾÃêׯo«Š&±ZX1ÇE8çÁpÙ²eê¿uêC4;òû ±|Á‚Õçò=ˆ¶·Öø·yž$ QᢧcLfÅÒÉòáƒGVê”)“:Æ 8  øNˆ˜Ò£©¬õP¿³koލ.üpDD;^¯~ôè‘zÝr3Ø•mY @à8qâœ.VGs>~V‰×³dI/[¶î×=‡ŒïOú¨îÝ{$›6»ªÃQcfÊãÇÏTùóç/²oÿ1¥áÞ¤q-Í¡”MN¾¨É!¸Éú ;U2x½åš#}4¨_M9þžæúÜž¨çrE[|™8yž3òù@—ÿ´ö,7oÙ#û\—KªTÉT³¥ËÖ«… wQæÌéT]8бèpøÏ?T=ïž[Z­eë¶½Zn4M³;ƒ–Ìó®vWÙ¶}ŸlÞ8WÊ–)¢ú°—Æ"qì€AeÖìåªm´hQÔ^ÿƒç¶wßQ• xë¶}Z÷XäÁg÷?®L›±Xîhý5¨_]o.?~’Qc~Wí{÷lkœw¤†#ÂQÑüáñðQïµk×öq¨pÎ>|XŒ|ÇŽjC#ü¿|úôéR¾|y£hƒ#‰*$OéŽÿÏ£]ïÞ½ ÷ðáÃõm0Þßÿ]éŽoÙ²E°añ QóЇG¤=tÊ5jd«yŸÃw0°ž4i’;$[0f4Õá€Ç"®Ã õ'8޽b³dÉõ= ÏÍ«7ÿ ›ÄïXôŸ1cƨ<5¢éß¿¿z«pÓ¦ÿ.$H mÚ´QÏÆZžF ŒH€H€B4:ÜCôã šÉáÇH€Bk Öà0?ü(ýå—_‚ÃP9F5Új‘¼p¶—,QPÓ9Ÿ%‘µcˆ&FðŽlrزu¯&ýP6m˜#¹r: œšýü'=HËú¿T•ÑšcrÍÛ,îëÖïP}êNÌÞ}Ç*gûo­êË䉔ƒ ‘ÆEKÔ“ñæHeÍA]´h>c¶îm\Ô i´dñ»¶/6ÚäÌ‘Ù_œípwí>\%b…ãbXßþãdøˆi* |¯vs_²h‚dËYAsxP‘Ö¨g{’$ ÕBŽaþ=w^¿ï/Þ\¨S»¢,^8^9_¼x%å*6Ñœ©çÔ3ÆÜ`p:Ã\º´1£<E ›Ÿ>SIA´üµk·$]:'U¬Ÿ>cp¶Ãñwõ’«rp£¢Ë±H3vÜlÃáî[Nsæ®”öíKÛÖ ÕóB¿ºáó]·Ž&õ1o¥,_±ÉÂá¾rõU­q£šR@[8x¢ZD¸téºZLÁE,¼{÷^›[*É—/»Þ­Ãí[¶l©ÔeAz¦L™kó@¡½î•åÉ“Gi#j’/øÎ¡'I5·¹xñ¢rޝZµJíõkÛ¶mSÅ8qâ(mvÀɌ͖ÁIçñ•+W”fyš4iŒþð½ÁÝÝ]%„‡ÞV­[·lÖæââ"Ø|2]S]¯çÝX%nm£GVŽoD³G‹͸ ç:â#FŒPQíx«Òz^9Úõ†ˆ†Çæ“9R°™MO’ 9J/ ºñ §Z5ÿ“<±5–r©u,þ@¨aƒê¢;ÒÝÏ=¬i“Z™,4 ì¢Þü€´>¯hC%ðAƒ dP¢G®®âßu¢D‰ ²"  Z!A;ÞH€H€H€H€B<=Ç­õˆ#"µmôö˜å¾zÍVUÎjXýŸ«ªý›ÿGã:ó;–¦]\¾¸äÑœ½f³÷Þæ6þQön¬?åË¡Æ -r³Ý¾}ß8„¦û·oߌcïú ʹ#ß‘­hè¨Q=ÞbxóæzN¿þÖKrä®(ýŽ—;w¦ý?gÖH¥çnLÒT°õÜF ë.÷¯–vmi¹<2j‰xïä`Jišþí4©#X@qjÚ¸¶êÙò j¿rµ¹þŸ#½zµ²J_ „‘lÎvHá+ú“Ê5 :Ð$+mÛ¶­Ô«W/ÐFU²dI•IN'öøïðÇW*I’$ ´±ðF$@$@$@^`„»×lx…H€H€H€H€ü@jMº‡)‰’Ñ RÿýP?´ØCÁû¹^éÖc„%|Hλ¬$W }ˆiXZMoç>’dš $Ÿ4mù0a~0ŸVÒ,'éc…üN³&µ¥¦ n¶OŸ>É?ÿü«iNÿhœž®IÐ@²l$Ú„®ùaS Gûu-R:ÓÐžÖ :î‚!i*"Ûá3ºœ;½MÓóO¤W•ŽesÁú3ih¾§I“BÓâ37yÊ¥~ýû¶°Ï"Ôûô«>›Ðׇ¤ æf– ÂçŸá™¿/•å+7 ÀÂ5tL9$ GrÎÀ6H¥Œÿ_‡À¾?ïG$@$@$à3:Ü}fÄ$@$@Á”À®]»”žf0>‡D>´íô ¢áð¶!ˆdddôÆ;2nüé§98u>ršÒ,×ý²‡¾;ô´‘|µåo½Uõ®ft§~DÿòÿÈwThÙª—Òwïо‰Lßßhc.Ü¿ÿH¦N_¤NÕТ‘óæÍn¾ì©ìÛúæÊ–)"ãï³—kR#µ Ù‘«WoIÎ<•äë×orïÎ!•ü2(H° ›;{”’å©T¥¹ 6UÊ”*,4 ¬¹/X¸FI¾D×´É{÷²”í1ÏO/¿~ýV&Nš/Ý»µÒOÉð‘ÓUT{¦LiÕ[µd°0ÈĘíÐ@üØR»ÝèĪð—¶xQ²t}µØrýÊ^Õø={ü&C‡OÕx~U ßËÉ«ùãíre‹ªÄ­ÍZtW£ƒÞ:º ,Êá®%X}óæ­Z”ªY£œÕlxH$@$@$@ŽM€wÇ~>A>º»w(-E$pÂkžþeˆ¨ùcí6ÅbŽî²·ÿuëw¢ g™"…ÿ¿:‰W¬‘Œ ‘5ñãÇüè I†È¢Ç·ˆ* Ióã\H¯yÃÞ¾}+W¯zè“ ø–€þ9òm;Ö'¯„ V† é*un'M3g/IŽì™5òÓ²mû>‰5Š—šÜ^õi}NL8Üõä¬õñ“A=ÜܘÞRµú¯Ò¨‰‹ìÚ}PœR%—ƒ‡Ž«2ôÄ;uhfÝ¥qŒ¨ëÑc~WÇhç“ÃÝ·õi…õ«©äž'Nœ“׸¦Ï=Y²ÄR¢xœ¢‘ @°"@‡{°z\?XüPBDYŠäIT’+=j¥³ËPÍÑ^RÄë_·øî~nß¾¯úˆ'–ôêÙæ»ûs”ðÊuáBydÁ"M[Tû±ûûÞC©]³‚|ûöM½fî(cå8H€H€H 4@Tv@HãÙËAØÊðÝãWMK‘Ì,ñ«!š;[6ïö¾í; æÙ¾yóWË”I}5$8Ïñ½Û;‹%²äÊåì©J† ©=óîD|í{76{Ì?8éß3gizüX`Â[ HŽjm¨÷æÍ;õ¦®ýª-DX'µnÃc   pDaqP“÷ðE¾jõ–Z²­ÙrHK¢T¬d=‰Ÿ(·)^WV­Þ¢c_¦\#Ižª 4mÞM¶lÝã©Ó]»ÿ”j5~•Ti Kœø9Ä9{9éÓo¬JP¤W>zô´ºWÇ΃ôSJ ÷øð±L™ºP ©%‰’æU‰˜öï?fÔ³U€„ ^‰….:¾è#ªfά‘>j€Úê çðÅ|ÌØY’1K)I’ü'©\µ…¬^cñ¥ÏÑÜçÎ]–*ÕZJÒù%WÞÊÚkµýåÙ³F÷í;—îÃÔñÛ·ïÕÜiò5mÚõ“ž}F«ó/^¼VçQ†$càñøñS™0q®¤ÍP\Š–¨«®á=œõÊxN5k·§´E$…S!©X¹™ÒaÕ¯ëû}ûJíºm%eêBjx>¸¿w†rýûu–Z„~èàÇk÷®­Ô¢4]i$@$@$@$àŸÜÜž)y›}®Ëý³[‡îëÏ?ËŒiCm:”zà8¸þ'H¤¨¤]7hßG±x`¶ÏŸ?ku2J¼„¹äÚµÛê $𥑠@p$À÷`øÔÈ2,·µWAûö'x=ò —#Z¢¥Ý®U’(hv† óƒÒçDR%èuâ5Z¤]6î¢ÊñâÅQɉ ‰>bätÙ»ïˆþÓ# Ó#·'ê^Ðp× ýãh¼†º}Ç~AT ’X¡Ý!MwôÔñMêUX½¾õÞ¥Û0Á+ÂY"ù T—!æ¨bÖõì9Æüoܸ«î‡×ká°Ævî|[2ÈEu¡ÏáÊÕ[2qò<õš*"­0ßÓ§/Êæ-{?‘HêÂ…«Fô7˜×uá•dD‚ëQáúyDàÀtMDîÀ¢E‹¢öörFåÁC&ËÀÁU»ìZÒ¬'Ož«¤YH¢†(©¶š¾' ‹º VéÎÎ鵃—êÜú »”6'^CöÊnܸ#=z’ùsG«gÖªu-ñlAA’4 ø'Dï#°"4Ù€þíš.’ζiÝP’$N`Wýà\)Uʤ’1c‰+¦þJ\¾@IDATÔªY^~kUßÓtÉž'OV•×9Kz<°‹ú½à©"O @0 À÷`ð¼"œÃЬ„æå£û ’]!â{ÎÜ•²dѹ~e¯Ü¼¶_G›·¸]ó»*»ti!—{W*K8^~íšÏúœgÎ^’«—\åıòðÞQ±Žè”Uk<¢ì›YF ë&kVNWõ7o˜+]»´´ªaÿ!þÜ·JÎަ抄c°#gÈUÍÁn¶ë×o+ýòG÷ɱÃëäþÝ#êÕÝ{š3½ß€ñªê^Íñ¾þ6 ÄÕæuL–.ž¨îa}uÍîíÛ5–+wËö- Ô%{9_¼xM ™¤t7÷ì^¦-Zl–{wË„qýT?àùðá£Ü¼yW%‘BÂÓ“mÔ¶Mr÷ÖAéÝ«­`ˆØ÷ÎÒ¤I)»¶/VºüˆÚ¢éŒÒÙî1^#   ÿ'‰œ©“…(ùB¯(AßmwkIpm9ÛÑÚó‡¬QõΤRK^̓çI€H€H€HÀ^t¸ÛKÊë!¹Õ¯-V#Ctw™RÑë±bÅP¯±"á¤BJ—ô8ÿ¾›1‹ñcûÈÎí‹d`ÿNƹ°aÃhõ=>xØ'séÜB’$I¨ªÁi[¡|1UF&ï,A‚xF"ÒÌ™Ó ÆëWC"¥üùsÍ5¬¡£¸¦ÿÌ[°Ê8¯º¹üª"óq;vL™>e°º´|ÅFyÿÞ]¯æ§}éR…ÔÞ8Ðu1íå+§õË—¯%‹&‘S³F9‹#±lÏÞ£ä/-BMr+E€Ü‡’  2èÎvŒàýû÷A3Þ•H€H€H€H ØìgÀ „nnÏ”„Ê>×å!fNœ @À8uê”´hÑB 7;sæŒ899I½zõ¤nݺ²v­Gbæ &H«V­äÏ?ÿ û²S   #J^:À¼3 „`ŒpÁ7¸M :æsfô÷a#ak›Ö %IâþÞ7;$ùfÍš%?~ÔÞÊ%Ý»w×’2gQ“Þ²e‹¸ººªó… ù 8C Á¬%épÁ›S#   &@‡{f÷AO²+Ðî§‘ €_ܾ}[5kÚ´©@’F$@$òX;ÜCÞ 9#    À"@I™À"Íû 8‹/J•*U$~üø*ò¼mÛ¶òìÙ3‹ûvìØQªV­*×®]“Aƒi9rKÒ¤I¥råÊ*¹¶^ùðáêމ'Ô©Ù³g«ã¥K—ªýÙ³gÕùéÓ§«cÔß³g*Oœ8Q®_¿.•*U’8qâÈöíÛUÝ]»vIµjÕ$UªTê¼³³³ôéÓGÞ¼y£ßÖØ#‚¾fÍšJΚñ+V”;v×õ‚»»»ôîÝ[òçϯæû+Vè—½Ü=zTuÈ!rîÜ9Å Éo‹[Ë–-UýÇ ätÒ¦M+E‹5ú‡´$Ò§O/)S¦Tó\·nqÝ\˜7ož”.]Z'N,%K–”Í›7ËüùóUÿ3gÎTU}bùòåK0`€–ó"»J²“,Y2Åzþºé}Œ7N:$ÅŠSŸ"EŠÈªU«T5ìË”)#É“',ª€;H€áÎÏ €_ 0ÂÝ¯äØŽH€HÀ¡À\¨P! 6¬–|9µœ?^ ïgî¾}û”“FfdA?~ü¸dÊ”ILõ°õíÛWà„~ñâ…º®;Ãïܹ£ÚÀY‹vÖçQÿùóç²qãFÅeÔ¨Qâææ¦ÊHÆG}Æ N,dΜYqøðá²wï^Ã^·Áƒ+g2".áPÆX·nݪ¶©S§*‡8êâ<Ö.\„  œÎÛ¶m“õë×  ÆŽ«wéiD°ë•+W ¸—wÜàȃD‰Éï¿ÿ®ú‹-šÚã>=zôP ±˜9rdÙ°aƒÚÚ´i#Ó¦M3îߺukS÷ƒ<ÏÍ›7¥F’5kV9yò¤$I’DÕ½wïž—,‘Ìó>}ú´zÞÙ²e“/_¾(g976mÚ$åË—½¼¥€çš.]:U‹GŽQÉvçÌ™£5„ # ,P6à„§‘ „Œp=Ïš3%   €&À÷€&ÌþI€H€…îeË–8’;&÷ïß—Æç0ðp¶wíÚU0ÖÈ­[·Tt6œ¿ˆ¸‡!J‘÷áÇWóX4€ãNqÚøðA•Ùg;œØ¨ó×_É¥K—T¤;¢º÷ï߯êy÷÷E´¹½Üà îСƒbçöåË—U„=œU+W®TsÂ0?8äñ€™Þp¶G‰E<¨ØÃ‰?~üxål·5N[,ÑœíÑ£GWÏŽz<Ç¡C‡Ê·oßdñâÅ]añeÒ¤I‚·0O,š|ýúUðÖÂ’%KÔÛø à< /4 ÐM€î¡ûùsö$@$@$@$ð=èpÿzlK$@$àPºuë¦ÕTìØ±•³ååË— ¢¢ÍV¢D %âŸkÔ¨‘T¨PA9l!yò=§/d] ƒÈs8˜áT†¤ÌÀ® ¦GÃÃ!ýÏ?ÿH“&M” ®Á™Ý®];éÕ«—ŠnGdû«W¯” KÔ¨Q•ÄK¸p/­999©zh§»=ænpÎÃyhqDê#©,¢ËkÕªe¡q‰öíÛ«Ûëîˆ ‡5nÜX ( Êøƒ¹!JÝ–Ùb‰¨°„²4ºa‘¦³ÔÏ#jþ×_U‡#F4¢×ñùùçŸ_<Ì †Å @è"`áN‡{èzþœ- ø'JÊø'MöE$@$dâÅ‹'9sæ´¸?ôÙ ùD0C¶D7DÃ["ÑQ ýõï1h¡ÿôÓO]”*UJEXϘ1Cɸ ²üÌ™3òöí[‹zxAKÝlpC~F7h’Ã1'œÝfƒ´ ìÆæÓ6˾åf}/D¸ÃÀÎÚpcÖë ò½ykË›7¯âc}ÞK,*$H@IÇ,[¶LEÛãx3Á–¥Ô4åÍG;,GŽŠŸ~-R¤HªHG›N„{=¬î¡gæœ) € ÃÝ¿‰²?  !§ª-‡ "Àáp×5×õÁÁikm¨ ³®k]ϧc$à4¸­ZµR&pê"º SÙ(pD¬ë†Èu’­zgæ1Z;ˆáPFt½O} ßr³ž›þæ€î¬6Y?§×N:LÆ7×E$»-³¾ê@ Hˆ‹ä­Xœ€>þëׯe̘1žº±õ¹@%ýíO x‚HÀnÁÂ$ú»ìnè`±€i6Èa­Y³Æ|*Ø–ñöt×®];ØÎ'   àD€÷àô´8V  / *ÉIüñG£™êò"Hæi68‰ !c6=Ûº®¹Ž=eݱ¬×…ƒzá±bÅRZãˆÚÖ­cÇŽzQíá@FÂSëhmhŽC«ýãÇJãõ`H±Ö‡ãèÓ§Ob=ÕÀêo¹Y÷™>}z¥ÇŽ96hÐÀ¢wœƒ¡ \‘ZóõêÕSçô?ÐÝ·eÖ÷C(g;ôìÍv=™«­~xŽH `¬]»Ö"ésÀÜ%ð{Å‚¶b+£Ã=¤6`    ï&@‡ûw#d$@$@Ž@Zí=zôPÑÓÐæF$õÖ­[ÕÐm-+sh¼#i&¢ÁáІ“R/5kÖôqJºãŽ 8Îõ¤œ¶B[ò .(u±bÅäÁƒ²páB£:’¦H‘BI£À}üøqÉ—/ŸŠH„S}ãÆÆ\ôä HŠZµjUAÂW8›!“çÊÎ;Úì:u2ú÷ªÙ—ž={ÚÍͺŸjÕªI¹rådûöíJ·Nuô¹zõj¥›ÅŽ:¨fp¼ëu¡ãþ`~ïÞ=)X° @—ÞË“'ÒÁÇ‚ÄÉ“'Õ"î¿oß>Õœ±hA#<H]¨P¡À»!ïd¥oׄY‰H€H€H€€€‡˜ª „C   ï!P¸pa%5²iÓ&éÛ·¯r¶ÇŒS,X`ó5zH‘¤NZFŽ©"±iŽ$Ÿ{öì±KŠ¥K—.J*åáÇ*ºò^"Ñ!w‚èt$eíÖ­› y*¢Aûõë§š!ò‘ÛpÌ»ººJ“&MäÎ;*é(œ×ÊÁX;wîlܦråÊÊÑ,Y25Ïþýû+g{‘"ETÐg÷Éô±íÞ½Û.n¶úÛ°aƒz³ `'M𤯠'zãÆ•]_ @[<Œ3[¶l ŒL4f®«NØø3~üx)Z´¨Ò¾1b„Z0€¦=¤uLõéÓ§ê MyŠH€H€H€H€H€H€”#Ü/;' hÕ«WsÒP%O-Z4É”)“§Èv}<‰%RNi8Êïß¿¯êÂAompDÛ²,Y²¨(kwwwùòå‹r¦£œÌ¶¬E‹*šþÊ•+Jw=Mš4!BUr0è'kÖ¬êcŸ?¾rÒÃ) g;ôúæþ!sûöm%M=’Œ&NœØ\ÅÇ2$h%H}D§Ûâ†{xeߨQ£Îïk×®)-ýŒ3zržÃþøñciÞ¼¹Ò¡7÷‡·`àG¯XBhŸÍŽù>zôHEöÃÑÃÛ˜¢ýqk«hác³6|v°ÑH€H€H€H€H€H€HÀ¯èp÷+9¶# pHpV[k{{7P8¨±ùÕ"GŽlwÓ(Q¢ºåæF2d0e8²³gÏn{W€s›_ Î|H¼|!:ß«¹ ßÓ§O 4ê1ÎS§N = +¼mÛ6%iƒöˆÎ·×ÁoÅç_ @{»`=      ðWt¸û+NvF$@$@$àHö ±"´õ±ˆþ›7oŠ®1ܧO%5ãU{ž'      G'@‡»£?!ŽH€HÀ_ ©'ôÞ!9š óoÛ¶­$I’$Ð0„ V ÷½úµkתd©¯ó23HœJ#      àL€÷àüô8v  _èÕ«—¯Û„ÄHZ:uêÔ@ŸZ¸pá¼Õgôñ†$@$@$@$@$@$@$àèp÷G˜ìŠH€H€H€H€H ø@òk$Ðvss$aFn,ÒH€H€H€H€HÀ·ø-Ò·ÄXŸH€H€H€H€HÀ¡Ì;WfΜiŒiÕªU’*U*ãØ«’9Ož7ŽšH€H€H€H€HàÿfÍš¥’0ë@–,Y"ýúõÓmîçÏŸ/...6¯ýóÏ?ò矪­Y³f2vìX£"á‘ðÙ·öøñcß6a}    `H€÷`øÐ8d     ÇŽ“ëׯ[àX±b…ôîÝ[¬Ù–¹ººJ×®]K•*U’òåËKäÈ‘åÈ‘#²ÿ~¹zõªº>oÞ<•ܹuëÖê8iÒ¤òîÝ;£- Ÿ?V‰ õ“Hmmª¡‘ „|t¸‡ügÌ’ @ˆ%°téRcnE‹UÎòG œêeÊ”1®™ +W®”ÿýWêÔ©“ôïß߸\µjUU3fŒŒ1B•G%¿ýö›üðòxñb£®^¸qã†äÍ›W†^Ž?®_âžH€H€H€H ”ÊæËé’ „ïß¿—uëÖ©Ùd̘ÑÂqY¯ QìºU®\Y/Zì;wî¬"ÞqòÍ›7~Òm·è$@$@$@$@¡‚#ÜCÅcæ$I€H€H€H€H äذaƒÀé«U«–äÈ‘C%K½}û¶lß¾]ž={&qãÆõ4q½ .ìÝ»Wµ³®.\8•PõùóçêRĈ­«ð˜H€H€H€H€<`„»'$}’U«·ÊÙs—åõë·’H®œY$W.gÕîÉ“g²yË-Šê›D‰I~®WE„ñ¼nuêÔÙ·ÿ¨º_ܸ±%ÿO9¤TÉB²hñZ­Me‰-êwÍ% Y}×àØ˜H€H€H€ÀÓ§OeÇŽ¾ºÃ’%KäçŸVm  óæÍSåV­ZIß¾}mö-ø&MšêÃ.]ºd³O’ €™îf,7·§R¨hm¹sç¾äË—]bÇŠ)[·í•Gž¨1½xùÊp¸ÃI^³v¹}çžqÝ9{9‹±gËšQ–.žhq®w¿1šsý”:W´H>Y³jºTªÚ\Ž;cÔ[²t½Ü½uPîÞ} -[õ’?I¥Š%eãúÙªœí-~íiÔÏ“;«¤Kçd£¾K·a2cæu.\¸pêgĈM-$$K–HÊ—+¦œý~™‡oXcH€H€H€BU«V߯J—.-pšÛ2(4jÔH ÷~äȹyó¦¤NZ%T}ÿþ½j‚èxï,eÊ”Æes‚Uã$ $@$@$@$@$`E€w+ < S§/’[·þ–}®+¤H‘¼jø‘4pð$5z¦…d œØY²¤“„ ãÊ‘£§åÅ‹WZyNùá‡ÿÆž1Cšÿþ_:t` Oh¾»=~*‹ÔÒúˆ§œépœoßq@>|ø¨õóƒ¤M›JÞ½¾(é2–0~С›fM먨ö+7Ió–=”CÞ|£Þ}Ç(g{Ù2EdÔˆ’9s:yøð±Ìž»B†›ªª¾{ç¡!ê×yø†•yl,“ @H °téRcÍ›7—%JÇÖ…bÅŠ ôÞah׿É;·Q šíË–-“_~ñœ’…ˆŒ×-þüz‘{    ð’î^¢á…À$ðüùK•˜*Nœ˜Æm#Dˆ #†u—¬Î$mš”Æy8ćí¦Ž;» Ñ$g®É¬™Ã•£Ü¨äE}BæêÕ[R§vEY´`œüøãªvãF5-Z¡ž-¹˜H‘"J„mÌ ÐçÄIó$Ožl²iÃC–L“Æ<°‹*Ãé>¼Çy¿ÎÃ7¬ÔMüAλ"O5-þ å‹;øh9<    $pòäI¹råŠBìØ±½u¶£RíÚµ ‡ûŠ+¤OŸ>J—½E‹2gÎÕOûöíeçÎR§NI‘"… ¶¸qㆺ~ìØ1U'J”(*¹ª:à    ð†îÞÀá¥À#P»V™=g…üT°†äÈ‘YâÆ‰%ñâÅV:î+”0ädükDÐi_¶d’M‡º_ïqì/iš–ÍëÎvs_ƒtHÝ”.UÈ|Ú×åÀfåëzÓr@çÏ_•“šÆý‰“ç´í¼:þúõ«4¨_woØñ €XDœW¯^Ýæw.3§ *H¤H‘´·?ˆ›››r¾—)SF†.—/_–C‡©7)‘$›-Cp¢ãÓ¥Kgë2Ï‘ €:Ü-p𠨔(^@¶o] &Γ3g/ÉáÃ'åŸþQÃé7`¼ôíÓΈ÷1ÆŒÝ_í¤c`©±eˆh¯Y£¼­K¾:ج|58Se8Ñ/]º®œêp¬cC$;ÒÚ2[2@¶êñ @è$àîî.k×®5&_«V-£ìU!jÔ¨R¾|y£ÝâÅ‹w¼‰¸~ýzY³fŒ7NÑnmp´×¬YSÚ´i£Éf¶¾Ìc    °I€w›Xx2° \»vKœ³¤—-›æ·~õê\¸pUjÔn-#FÎ>½ÚjR.Œë(„ V»»(Q"[\óƒ°aÃ(©ë¾nß¾g}JéÊãäÙ³—U¢UO¼9á›yø••7·ÿîKHFvåÊM“sýœœ9sIÓÌ·í\·uÃŒÓØ:Ís$@$@$@$ DŽYKl××4 £ËǘãûWݺu•ìÌýû÷åÎ;rïÞ=Á}’%K¦åôI«%½anâe9Mš4Z^¡^^ç    ÐC Lè™*gêÈÅ^²Lyûö1̘1£K¡By´„¨9Ý·oß×ôBŒèÑTñæMË_ˆ¢ÞízPÞ¿÷HPª×÷í>Iâ„rJ“?yúô¹Ñ²(3~ÿ/Y—~¡°6Ö âʸ säï¿è§ýæ-®’+oe9ª%zµ6ßÌשּׁïé×c¼yçú’¥ë¤S—ÁR¨hm‰;«8g/'M›w“iZÜcÇÎøÊÙŽ±0ÂݯO„íH€H€H€¾‡rö$Ož\Š)"õë×HÕ ±ª½Îöï¹7Û’ „<ŒpyÏ4XΑÐÉš£¼ è×Qrj:îÿþ+²cçÙ´ÙU~ÒœîqãÆö4·2¥ Ë !“¤Aã.òk‹z*â}ÿc²k÷AyùòµJ^ xD˯\µY>~ü¤¤_Ñ4iò|£¿L™ÒhÚê…c½€þ÷ì=,¥Ë5”†õ«‹»ûGY´d­H€H€H€H€H€H€H€H€H ÈÐádèyc3رc t2Ç/ÍZt7_’*•KÉŒiC-Îéùòe—•˧HÇ΃¥C§AêtĈ¤\Ù¢Ò´I-³vúÌEuÑéºuv¢¥pá<6îÚ7‘sç¯È²å¤Û¹ʡ߬iméß·ƒ4nê"£FÏ”D ãKûvU_Ê—#ÿÖíú)‡´~ƒX±bHçNÍ¥_Ÿö‚È}k³whçWVÖ÷ôë1œã˜/’œb±cúŒ%½öï±8qbªÅ•¬Î$iÒD½{ 7t¸·'BÇ;yâ>´›J8úæÍ[MŸó&#ó¤L™Ô¦ƒÚŒ‰H±AöèI’$P‰°ÌuŠË/ŸÜ¯šOÙU†ó~É¢ šÃˆÜ¿ï¦"ÙqÖ°Au›}äÉ“MNÛ(¯_¿‘«Wo)yŠIÔb‚Íÿ?iÏ\6nÚm«šq®l™"9r$©VµŒq7·§*iíŽxD¸ß”gÏ^XÔóêàãÇOêÒÅ‹¥cÇŽFµH‘"I–,Y ¼îˆO•*•ZûF', îܹ#ëׯ7ŽYp ÇwŒp$@$@$@$ŠÐáŠ6§ºdÊ”V? ÚYi´d§ë×Î’={Kg—¡rþüO eál·e ÆS‰t!Wc6$Ö…Î»Š„GD<¤f´=þý÷_sU›å>~€Zÿ .œ¦¯iÃÿ?^ß§I“FË1Õf_V¬XÖUyL$@$@¡†ÀÏ?ÿ¬°_½zìç|ðàAùö훚G„ Õ"|°Ÿ”68Û»ví¦Â9 @° @‡{°xL$ pzÿÚòg©W·’ 1M&M^ Ÿ?֜ؾw¸{E#B„Jƒ^ס.¬ 2YZµj%:tÐ"á/[l7nÜׯ_{ÕÅùÛ·o ¶­[·Zœ'Ž– ÕÙ“3>qâÄÔ‰· Å  HÀÉÉ)ÄHÉ`]_8¨T©’Ìž=;$>2ΉH€H€H€H € ÐáÀ€Ù= €%D§ÑSZµüEºõ!3¤¶¬G?üðƒdÈAmÕ«W·¸Ãßÿmá„×òÏž=³¨çÕÁóçÏeß¾}j3׉9²Mø”)SR'Þ Še  pø¾ ›=uz]îI€H€H€H€HÀL€w3 –I€€“Srùcõ ãÕí@»±Õ’'O.ØÊ–-kq‘ïçÎó䌿ÿ¾]ò4îîîò×_©ÍÜ1tâáü7ËÒ  ø(Q¢˜«²L$@$@$ˆÌ÷@¼-oE$@$@$@$ÂÐáÂ(§CÁ@ذarÈ1bÄÂ… «Í<À?*w=^ßCnò>tâ/\¸ 6s]üÈ÷J'>f̘æª,“ 0F¸0`vO$@$@$@!˜î!øárj$@þO bĈ’+W.µ™{GRÖ«W¯zŠˆ‡Nü›7oÌUm–ñÃþÖ­[jÛ²e‹E¸qãz©oQ‘$@$@$@~&Àw?£cC    :ÜM0X$ ¿@RX]&ƺ»wïzrÄ#2úïöôä÷îÝ«6s}HÐdɒŸ¯~èÄCº†F$@$@$à7Œp÷7¶"   ¡G†Ÿ `)R¤låÊ•³¸Ó«W¯lêÄ?xðÀ.ø÷ïß˱cÇÔfî8|øð^êÄ#™+H€H€B3Ì™3ËÍ›78Öuçú—/_ ,óçÏ—… ªkúu\ìÔ©“L˜0Á¨Ç X ÃÝšI€H @›½H‘"j3ß:ñÐy×õáõ=tâ?þl®j³ ‡ÁùóçÕf®€W圜wîÜj3æÛ·o^êÄ¿}ûÖ\Õfzˆèöyóf‹:ñâų©Ÿ(Q"‹z<   àN I“&2hÐ #²ÝÞùdË–M²gÏnouÖ#   ¥èp¥žÓ&~† +™2eR›õèïܹã)"‘ñ/^¼°®jóøéÓ§²gϵ™+DÕKxŒ‡F$@$@ÁdÞŠ/îéÿy>̓Ñí>âu     ÃŸ ([ùòå-f‡;äetY}x³&­E#ÓÁ»wïäèÑ£j3–üÑKøH‘"™«²L$@$@G iÓ¦¾r¸#yýúõn €ã ÃÝñž GD$@þF vìØR´hQµ™;ýðთ:ñæ¤qæ6æ2´äÏ;§6óyèħNÚ¦N|ôèÑÍUY&  #P£F iÛ¶­¼yóÆ®1 ñyüøñíªËJ$@$@$@$@¡›î¡ûùsö$@¡”¢ÐóäÉ£63‚¯_¿z©hwŸ Qó7nÜPÛ¦M›,ª'HÀ¦K–,j3wˆ„«^éÄ¿zõÊ\ÕËòƒÛîÝ»-êĈCéÒ[KÔ@'ó4  ÐI µ¥K—–íÛ·ÛÀèv›Xx’H€H€H€HÀt¸{‡—H€H€À?ü NNNj«X±¢ÅŸ={æ¥N¼EE/^¿~-‡V›¹J„lFÄC'×h$@$@!Ÿdel9ܱ Û Aƒ€3$   ðWt¸û+ÎÝÙè1¿ËØñ³eÛæù’+—³CMöæÍ»ò÷½‡âîþAÒ¤N)éÓ;9Ôø8 ï#7n\)^¼¸ÚÌ=A‚æÂ… žäi éŸìÓ§OræÌµ™ëÂɧ»uDožl’={&¹s羜8yN† +Ù³e’T©’yšÙþýÇäêµ[ê|Æ ©¥pá¼Ù…#GN‹ëÞCZù«Ô­]I²eËh´…|Õê-Zäç[u.|øpòs½*1¢çd‚/_¾–›v˧OŸ¥TÉ‚=zTY¾b“\¼tMR;¥Íëjú˜ß¯} ©Ì÷Öí¿Õ˜Ò¥K%–Ñ-qâ%JdýÐÏ{h;ïÝwT“”¸-×oÜÑØ†‘œ9²Hý_ª*ÎæŽ1÷õv*†¨W§vE‰-ªûHv»ÒžÍyÉêœAš5­-áÂYþ³C[<;7·gâœ%½zž'µú'O]ÐæIjת ?þø£q»‹¯ÉÚu;äþƒG9r$É>µ&eGcpGºwkeÔcHÀw S¢D µ™[¾{÷Φ#:ñß¾}3WµYþøñ£ö&Îiµ™+à¿×^éÄ#²’F$@$¸åîââÄѸqãÀ½9ïF$@$@$@$bXzþBÌ´BöD._¾!¿þÖK9¶kÖ(/1cF—¹óVZLºQÃ2iBMæ?§Mï~c4çú)U¯h‘|óJU›[èŸ/Yº^îÞ:hD™?}ú\šµè®œûú àÐÏ‘#³~hì>!-[õR‰ !ïrþÂUyòä™@ÚŽý)ÓÊ™“[$v옪 ÎÕ¬ÝFnß¹'=Q眳[ê®gËšQ–.žhÜ…#§K¿ãs&ÎlºaÁàô‰-ú¡Ÿ÷9óTÖd'n¨öXÀ¸wÛõ>×å˜{ë¶ýäóçϪ>·Þ¹{_ZüÚÓÂ!g|ófu1-\ô‡tìM¾gÇÖ…J¶G@®\ÎzQí»v®8íÙµLIÃàdºtNR¨`nÉä\Fã 4DÁýúUsþǶ`„˜ƒûWÉÂEkÝnA˜$4ðßHÅ`«\¹²Å >|èÉ„­nnnõ¼:€ÃþàÁƒj3×ÎpÆŒ=mЉ7KR™Û°L$@$à™@³fÍ4y¿ìž/ð ØI€w;A9jµâÅò[8Ûõqvsi©îû³p¸ë×!»²lÉ$ ‡®~Í?öˆêÖíè/jÔ(JþÅËWþÑ}€÷‰ÈÔL¾HîÞ}`ܺ찗/ßçlæÏmál·®'>äjºjÏ :ìf‹7¶TªXBöì=l>­ÉÈÄSr3xÓàô™‹ZD|r¥‰Ÿ$IÉœ)’“±hÀ ‡#8qbÁV²dI‹±½}ûÖ¦NüÝ»w-d©,™ asêÔ)µ™N«ÿ§M›Ö“#>]ºtZž‰hæª,“ hªV­J$@$@$@$@$ð]èpÿ.|Aß8Q"Ûz¾H CrQ[SÓvHù5Ývk .¬@j!8¤càp/W¶¨th×Dé¨#zºôд÷ÉbÇŠém•?©ë^Ežþø£g~h°î™2vül•4õÐá“òöí;ã>½ûŽ‘c‡×IÒ¤‰Œs, p~çÏŸ_mæC'ðÖÛ7ÉX}2èÄCgÛºuë,ª'OžÜ“#QòqãÆµ¨Ç­Þ¿/îîî¡uúœ7 „Jx«òm4   øt¸=h{æìe›£8uê¢:Ÿ6mJ›×í¤ ïîþ!ÈeQà šùûR¥ ¿yãÉXÁ šóö8Ü}â'N,¥Ÿ¿i³«¦ßÞQIöèmý¾}Ç~ýÐØ#êþÂ…k2d‹Úpc½uëo™¡wâ¤y2kÎr<°‹Ñ† àM‹rÙ²eS›y&øïW:ñ¯^Ù÷&Ñßÿ-ØvìØaîZKlÛKxÈåÐH 4ؾ}³T­RC>k Þi$@¡‹@ß¾}eÈ!¡kÒœ- €¿ ÃÝ_q~gçÏ_Q‘ص䤺!ùhç®_«V)­Ÿvè}ŒèÒÐJÏš5£1VDwøó/•L5J”ÈÆù€,¼}ûÞSòXý~ƒ†LÖ‹ß½ïÙ½µtê2XjÕi##†u'§dZëMqé6Ls‚=”äÉ[ܺû…‹Õ‘9³FJ³¦uÔ5,T¤M›JÖ¯®î/^¼¶hà Io(A*[•*U,&ùàÁOñˆüø±E=¯^¼x!þù§ÚÌu"GŽl3":ñám¼ÕdnË2 7G£³=¸=4Ž—ü‰þH#   ï!@‡û÷Ðs€¶É’%–½FÉú;•SúåËײaãnyòä™´o×Xi~c˜¯^½hCÊäáÃÇJ×wÒäùÆ 2eJ#¥K6ŽQ@´ùÒeÔÇ'OÇN–¯Ü¨œà(G‰I5¬¡’òÁÑ{ÀŽýuF½\@IDATök‰C‹Í§Ž—.[/=Q’2‹¯•õ«YHÚ Ñë !“¤Aã.òk‹zj|Пߵû ¦—þZ6m˜#+”P}Á)¿aã.5œ8uú¢Œ7[]Cðe‰âlêÚ« vü‰3º é–­{¤I³®R¤p^9~âœlÛ¾Ož={©æŠ¤©˜GÝ:‰h÷î;"çÎ]‘k×o«;¬Y»MΜõH>|8­^%AT»ÙÚµm$W¯Ý’3—ÈÆM»KÐ×/¨%N½wï¡q…OŸ>«ã¿ö”¿ŽŸ•š5ÊIšÔ)圶è2zìïêZåJ%ÕžH€B/$I’¶R¥JY@xýúµ–œú¢'g訉MY¥Šß©¨íïäÎ{*ªþ¥ŠÐŒi£mnTnѼ®` LK‘"‰ìßã¡õÑ?~ªœêÉt$;ι|©O4½õüëÖ¼s°[Ë]ÙΖ-ƒ`±Úõ·ô kY‚qåÌ©ÍúåYHˆ{÷î}‰=ªÄ[Ï °Öç6 ¸B³t2eʤk}D½Ãéç»uñööDËûgx)Šèy,kÖ¬±©‡{Ú´i}IÔÄ‹O¨oƒŠ;$@$@$@$@$@$@ÁŒîÁìû† rP08}™þ±íرӲ{÷!q$cf`¡‘ @`À,œ)Rè¥\¹r6§¸zõªÞpÈß¾}Û¦ž³{÷îÉÖ­[õb­6lX_NøÔ©S«I¨oÅm      K€÷ ûÑ8¦ø{¬Ø¨¥^ >qÒ\]9“ŠHÏž=£ó†<$ <^k¿C2æúõÛ²tÙ: >¬téÔ4HŽ—ƒ" D¢c)^Ü61÷ÇêÄ_¾|Ù%øgϞɾ}ûôb¥Œ¤¬Îtâᤧ‘ @P!@‡{Pù$Ü’sV¯ÑJ;/ví: X`H<ºfÕ47z ÙUŸ<ºÃŒuI€l DŠI%}Ϋë‘—/_:Œˆ?wîœK:ñÈ?râÄ ½XûÅL¤„ :ŒŠ%е*·I€H€H€H€H€H€H࣠Ãý£`Ø“ ŠýÉÃcòúõk³c8è,5qè+WnH½úm ýÙû®TA™:e¨?µöðœY£t‡p(!Š3 lôØi²jµ§[]mZ?Ki*§p« +“ €+¾ùæ•Ì:³^¬õÿþûo§:ñ?¶Vu¸ ø‹/êeõêÕ6u¢G®òo¤ñåŒGd>H€H€H€H€H€H€‹îE6ûýöÛo Í94i’Ë­ë{WbGÊÙŽËúkò vu ø&*T(I™2¥^Ê—/oSáÊ•+£âïܹcSÏÙêmÙ²E/Ö:áÂ…óå„7tâ¿ü’·EVVÜ&     pŸŸ,ÝgÆ$@$@$@L ~üø‚¥D‰6gzðàCx8èÿù盺Žvž>}*{÷îÕ‹õ8^z"I,œïÖea„±Vå6 8%@‡»S4<@$@$@$ÔDŽYòå˧ëØ^¼xá0":ñV 6kë6d½Ž?®k9$Û%JdㄇC>UªTBx+)n“ €îü=   ö¾ýö[•ô:‹^¬ø3gÎørÆ{{{Ë“'O¬UnC'þÂ… zYµj•M˜1cúrÄÃ7îû'½¶9wH€H€H€H€H€H€‚:܃ÝGÆ“ ¸J:ñˆFÇR¡B›f—/_öåˆ?yò¤Ü½{צž³[·n OOO›*áÇwèˆO’$‰`<4øØž>}&+6ʆ;äÚµ›òâåKI’8¤HžXrçÎ"… ær:¤ ·Ëýûôñüù²KìØ1œÖ )–,]+oÞ¼uz9_~J’'K¤¾W’XÂ{§'ã    `G€÷`÷‘qÀ$@$@$@A A‚‚¥dÉ’6ÝݿߩN<"Þý3DÎïÙ³G/Öº_}õ•SxDèÓH 0,÷Ø õêw‡Ût¿mÛ»Äòù”#½ï¯í¤`Áœ6u°Ó¥ÛÙ¿ÿ¨.÷X6I¾]ÄWVP¯~G5æ©¿—…DË©S'“î][H•Ê¥ý­Ï $@$@$@$@Ÿ:Ü?Ï9È^åÑ£§äÜùËÜùsHäÈÝ+³d/#¹se‘Y3F¸Õöĉ3â}æ‚n“)c¥ÑÏ­ö¬L$@$2 @›=þüz±^áóçÏFÄC'ðþ´ä;¦kÝ/¾øÂ©N<4ëi$ð>ð; gùïÃ'ùÛ|ûö½R¸Ø2cÚïR³Fy볂·oß îe«ýØB6{Ö”±£ûè{Zò!   ø¼ Ðáþ~þ—”)“H˜0Ÿ>š®zÍVÊyqV Ɔ ª»õ‰Ü¸q[.^¼ú^Óyë5è(ûöÑçkÞ¬ŽŒù«[çfe  Ï‹@˜0a$kÖ¬z±^9œnÎtâŸ>õ?JöŸþ‘óçÏëeåʕ֮%V¬XåiâĉcS;$`O N½ö2oþ ³¸Háù ê7ì$ âÇQ/›r˜í>÷òåJH‘¹m0¼}û·\UÒ<³ç,“›7ïècãÿœ%Ŋ敊JÙÔå ÀçG€÷Ïì3ùò•äÈ]^Fï)ÍšÖþäWpß Ù±s¿-^S\˜¥ïk¼I“&Ðב>]J_Çü+رu\½zSÒf(¡Îí¿D€ýñ8 ÀçIÀGV"µvŒ[ à‹3ø{÷îY«:ݾyó¦rèݔ͛7ÛÔ‰!‚CG|âĉ©oCêóÜÙ¶m³½Aýj2þ~6¿)R$‘í“ÈeŠê{CH¨ *¾Mû¾²Çç ÎÁUC·¾E󺎈üÚ³µdËYV¼½}fLøîI±H€H€H€>/t¸^Ÿ·¼zõJþþûo¹}ǵýÀÆ=ÛøñbëÓ(U· íÇŒêív;4:´ŽðÂT~ 4È¥%L˜P/¥JÙF½Âá~üøq_5W¯^ué%ðãÇe÷îÝz±Žû믿vªÿÍ7ßX«r;„À‹ž¶ú™WWú»Â2ñÏßÌ}û Ìzô['iÖ¢‡>„™H¬7n,ûª~îŸW.^‘Û·ïIøðaÕý]IŸ>¥¸sŸ…ûTb<{ö\%%M¬~—»ÔãݧtæcÅŒ.™3§Ü~ .¬´iU߆®‡ööüù %?sZnÞº#ØŽ;¦¤I“L¢Gj_Õéþ‹/e÷žCråÊ uoýŒ÷СrCEÜñÅÿ$^ÜØš¿«3Z/]º&Ó32eJcórÆé€,λ¤Ú—páÂÈw¥ YŽp“H€H€H€>?t¸óÏüøqoY¼d­šÖzCKĤJ™TbĈª¦¶_”_:6vzuîDtŸ>}^=ÄÑzçׯßÖòªU¾—T©’:íÿÀcâ¹ÅKN{Ÿ—hÑ¢(õÌjšm>™>c±üXýõ Îi[—P¡B 4è1=œF$@$<DU ( 눟={æË òäI-7ãŠN<þ¯=zT/Ö~áôDô{êÔ>‘øÆ:UªT)R$kUns+6 îà ëØþgcÓé÷“ÿš/¸W‚Áñjuæ:m¨Ìç!ÔN¼‘XÕZ7a¸ҺåOÒ¦u}k±L›¾H~RÒ~°vmJßÞíôþ²åëUpÈk³næÌieÌÈÞ’;w³Ìº1ÁJiß±¿~A`”ýõWºÏÞ½ÚÈWߦ0Šåî­%JÀÿ®'OžÈ<]»%I’$0Ën){^¿«+}%bÅ=\™ï‹Èà%yòÄfl$N–_à‡½z~ZV¯Ù"M›wÈ)ÂæÏ#•+•Ö÷šmÕ¬$ÇÅ}¢Õ"F /?Õ«"ýûvo¿õý ãýMEå=UîX‚q† # ä”?Çõ÷õ{`ýìºtn&i"›vÓ¿8wš4Éép·~Ü&  ø, ÐáŒ?ö!C'H§.%fÌh:iè›·eô˜i-XXófµ7Ì0<àaÈÐè7~–rj¯ÑÇð'Cw•Â…l5*ñU³víØFO„áäúõ[ZïsÉ¢ñò}é"fØ€c>ãÆÏÔå˜fo<¼á¦ÿÑ£'?~l?oÄáHnÞ²—ùP‚d¦ß—.l­4aÒ\›$`˜½lÉD›±XwñSç§ö*QÝi³ãÆö3÷ßw£U›ÞrV9Ëq­`5ãmÚ¼K'Òzöì…f0iò<¹xn›z!ÍWÎÌZÁ¿ö©ÄÃGL‘î][ˆ£ € çèûØîÝZH±{„³–p¿ mù¾ýG«hëæ¥ÀImu¶·h^GªVþ^GcïÙ{XG”#0%° ÷«1,Nœ˜*8%¼±+=ýÝt¶GYGƒ#¸%^¼XrLÍRý¥ó@}ïÇüŸg;})z¸·BÿTB[DêC²gÊÔù¦³÷éÃwS³IuÙŠ•›¤wß‘z,›6ï”Yj&´ü Ãý¦álÇý:¢à1cÁ¨À$„E4}áb5äÂÙ­zƪÑÖX/]¶Nð\ç|ù²I†ô©Õgüîúz\“ ÀçF€÷`ú‰#R‘;.1œí¸8f·o™¯¦ê.6£ÛQžO%|ÂòèÑc™1s‰ž¾Ú»W[òÓ0• "Xnݺ«|pS^ªdA=u‘܆N&n¸Eie“t¤7:?ŽrŽ·ÓçÓ=thÇ¿vÿüó¯té6Xð€4|XiÙÂq‚*cÀ†&¥ýí:ôèZnóœ/yóf3šëЙì?`¬Yö¾˜Œ©*•}œÿXÃá^«Fy¥ÝWE F×]#ÂËj=º·Ô»ˆ$ÂKDÝ·hVG;Ü9i:ÜQ ׉ÏçÚ¸n–þ\Œ¾JϯðºhÇû‚…«ÌqÇchKDVÙGªcÑ^îF–áåH€HÀ=ø¿(Q"½|÷Ýw6ïܹ#'Nœð匿víšK/›=z$^^^z±v -ø)Rørƣ̑vµµ-·ŸÀÉSïî˜%X¶z§Ùu‚qdÖŒ6ÔÝ”ƒšÞK–®Õ÷‘Ðf‡ž¼½á¥ÑÈá=m“"¢3+©;rô”Ù ÷p@G½qŸ‰2Ì,Z$NfŠ—ÿj×oÜRâ'mºÁK“*j}Ĩ)Z7Ý8ù«­ZíiîB6§Iãšæ~5ãñ¯IƒÅT¢3Ã=6ä'Odsߡӻ—íÚ4PU9tâ€þ⥫‰!Øú ÛM‡;fü5u.‡D$f7Ñï¸oÏ“'«ÖbÇËÜ£W÷÷pÈÛÆû‚ycµt¤ýqî“ ˆœõ>'ÇwEšt©%Eêän·c  AÀ±ç3hŒ£ðƒ@L•ªPÁ\Jr…ý;`?Ž÷ÙÇï  i$@$@G zôèJN¤ ^¬½>}úÔ—ÞЉ·þ¯·¶±n¿|ùR9 èÅZŽÿsŽtâ!O!BЖ;³^GpßF€„aQ"G46|7O6Y³jšî7‘ .0¶ÖaF¡a:¾¯‰¬ÆØ´I-£š¹ÆŒDÃá~áÂU³Îâ3g.è}8åéà;gòµmÝÀ&ßìÀÍa¿O,þYúô©¤YÓw×À„)Ê¡n^Ø›+|БíMâK‡=¾EfgÔ˜©zv5§Ú82Ô5fxâE€£Ïö‰*zõÆþ1Cºtjª_‚Ø÷‡û{äi¢‘ 8&°~ÕFéÙñWÇý(íÚ§“r¸·ö£FÐ>t[å0¹¡¤da‘Upb‚DÌ9´?1Ž.  øöŠôØ_ €†úÐß'ꤩˆ”[à í²{ç_z—ÆqWÖˆRÆ4R<@·jYOræÈ$©R&‘[Ê)ŽcªÑ±0$$udˆÂƒþ¸3Cä;¦;]†›(”ùC ýiɯ¢û¦ä:z9ਮ;eÆC ’ f‰€2Lå…ó`ÉPñÀsTéÌC¢Æ™ÑòöÇñ0CÔy`Îã±t’Ž.ûsÂì9’À:Jæ ³  áÂ…S³Ñ²ëÅzBooo_Îx”=þî…±µu’gÏžÕ‹‡‡‡õrÆñG|¬X±lêqçà $K–Pöìy¨;ºò߃î‡÷껇ԩ“©Ï4™ÍÜ z+g8rϬ\µY6lÜnsÜÑfNâ^ÓÞ¬R‡øÝ2 ‘å†!øNwGæ,ÀÃQÝ-küs =;Ó긆 f Z 9ÈûF‡Ÿ?'̱vºýsÃÞ'U®ô™¬t÷îC’œYãЕh>k›€ØÆg5^}N˜Z$VVG¿î‡_Gvà€Ï4µäÉ9: eˆÂGÂYhÐ7lÜÙWÎwO–;Wfw›°> @€ƒ0mÚ´z±v—È/^ôåˆ?uꔺOpM'þúõë*ÏÈuÙ¸q£µk•<’CGò%W NÓ¥M©œÐ):µ¾<·xi©ÀíÊ©$²FP†qÜ•µý‹ £MÅ ¥yº÷¦óáùŸ½ñùƒaþüÙ¥[—æj†©óçÇý¯a•«636ý\#€ÅÞ’&M`JKÚã> ¼áÇ«¼§¤V­Zêw×ö…Õûô÷¾m‚Ê8Þwült دV×^TfÈ’!è^GF$à/:ÜýE4+@31¡ª2iÂ@©ÿSU=H8Ó“'O,µkVÐ÷û÷}ßC °çÎ]öuaˆ¸D äW`7Õ ˜ýêÍœµT³þ@y̘ÑdØðIR»VI ®õ°¬X¹Qzõ!cGõ‘\¬_|ñ?]ÿ÷¡ÝåîÝÒðçÎÚY^®lq›~ÜÝA„w3ÍFþÕ«WÒ®C?w» ú˜BŒ('{¶è¼wßQ~ž2?ˆŒo«´: Ë‘¶|Þ(/£OWÖ æ”#WëädÐïǃÞût`‘x S’á|GDH€H hÀÿ*HÅ`)]º´Í nݺåËyèÄ»b>”]»véÅZ:ñ)S¦ô匇N¼‘;ÆZŸÛï¤N•ÌÜ“÷þÍ"Ãì†ê5Zéºh¼pþþ:ܤôVt8†S¦H¢Þ—)]D;‚ápH‹éLÎë×oœvýè‘Ï˧\C†T’4IB}¿ƒèóJUšúÛ¥U:ƾr»¶ ¥ÆåtRTè´ïò:hÎz…s}«Ò†Çòǘ¾ZCeÿýî~ ³ ß÷å–_ã²'÷IÀ+W®Ô/f³fÍúIîAe®0càEà•³mȘwyH‚×è9Z wÐáî­ TÉš`pJïÙ{XIµ”’dIé¤Rƒ‡þ©ýPÆw”9š¿+UH9¿7Iã¦]zà˜ÒºÙÓKöª~ £ÄI°bEóʨÑSåÇZ­¥~½*:Ùéšµ[eÛö½*a“Fùä)óµ&;œôˆ&G2§²åI¦¬ßK›VõÕÃZJíAf3Xaáš¹R°è»¿Kã>’T±Ó›Ïà{Oï’Dj6•agOŸ•6;È>¯ýf_8–4yù¹eC©ß´žQÕ\gI–C®\ºª÷/<8#;¶ì”nm{È¥ —Í::—G›Ÿ¥c$àþF——Ìû½Øó.@… f-Ò ¶ÑO6¯Mí`ïѾ—¬ñX§¿uá?"¨€ÎõªI×¾õ÷’q ÉXÛ4jgìJ¾Âù¤YÛÆæ>7H ¨ Ã=¨|nŽr-gNmVÓ»ãëhjhtÂé]½¡„f¥¡Õî¨[èo/_:Q£]´hQlœàÖ6Ý»µP S몿 ;v usô.¹Ù«ç§­UÍíì*IÓ¾ÝË Kñ„Ĥx1àè&}Ýšéf;ëÔo^úDÞX˱=K9ª±¸cÚÙµ}±fýLLyF„ÌÙy\íÿè¡5«>b«µîµÃçÁר ‡ñþ=ZwR>iÒ$3#Ùñùøe˜‰`¯ýîWýu ÿp{÷j+•UrÜú:ÉþýGý=u±ÿµz9©"<ô;3ÌlÀb¼\ÂKDø-’G¯1›’I4   Kÿ'Ò¥K§ë(áÀu¦ÿàÁkU§Û±Á²aÛ:‘#Gv與C ÎÉhúuT/­WkG+òûä+XUÖ®šf܀놼^Ûöý”óêó|È ÿÂãý¹C¯ q„Þ¡C'üèåýáþ ³51+V¯AYåñ—îÐêtå!'ø)íÊÕëæéÝ—ãžÇ‘D£ÙÈŸ Ì0?a–ܼyG×D`‚}’VÜãC¶æêÉsãĉ¡ëâ…Ì’¥kõ6k Gh?“pÜø™Ò¼eO]'wî,Ò ~5½ÍïܸqCðâšã#FŒÐI“&•£GjÇûŠ+oÈqÁðÝ„ï¹Ø±ck'0ÊðrýÀ™|úôiíTB. HsÁ9=oÞJ”(èZF-mÚ´ÑÏ…éÓ§WÏAweÔ¨Q²téRY½zµvR£ßpn#§ê;wNøup %K–D5—ëéÊ–Û·o—bÅŠéïô`„›7oÖŒ/àŒ‡á:ñrå÷îÝÓœQ>hÐ õ;›‚5Ãgà(ŸˆñRö÷ß×õ]aç×8\¹Gß…úäüAïI ºz^¸xAÙ°f“îaùÂîëVn0íÙreµq¶/[è!­¶•gÏžûŹ3ç¥S«®ròø)íG #[ë±VZ6h+ ³þG #oÔºÏ_­‡üÝF¤|¹¢åêeÇ2„•?iüȉr\ÍüŸ½|ºéСò®_ý.'P$åo¢‘@P$ðEPÇäD£ 2ÿØáx‡£;Q¢x~:Û­=ãË4Y²DNíFÝÂ뤩Vg»q̯5$fõŒs8r¶ûÕ6°ŽáÁÎjÃÙXçqµ_|vø3gNk:Û]m”ë!ªÊkÇb8 “º®¯üjs­úÄFyòð˜x,›¤gZ@êÈ?½Y$ïZµz³`†Ažü•$Bäôz}DÇ_»æs3îçÉyH€H Hð™é–DG/"ªròäÉÚqçœQ›6mÒQš-Z´ÐÎ&DtºjpØÃ …>Ñ7"A!K6lXõÿ7³Ô¨QCG‹.\¸PÍX;®8®öTëáž 9q Ésç.IÆ,¥¥TéºÒ¯ÿåè]¢dgJæletwã:6¨æRâ{¼ôÆ=' ŽãŠJ‡³øn«H¼3g.h™¿ÂÅjÝêõ>õÁaÝ»¶0üxŸ>S)-³ˆä¡EÔyjÐQËĹ޷¬YÒ›M'Mž'ƒ×ln©È¼Íž»¤|ÅŸmfò]¿~K.(‰D¼qÕ dX­:meà q:8Ÿ^¢ÔU³; g;‚_pO+_®„)Aˆà˜œy*È"•ø8Ð{ïÑk˜¾¿Ò•ÕöJ'žæœ€···/^\WÁI~õêUõ\–]I.]Öêö-'Mš$­ZµÒö5kÖHÇŽõ6¢Ïñ}Çó;w¤jժڹܤI3(¥sçÎÚÙŽèm8QNg|§Áa‡: Ns|ßÁ‰¾ÿ~½ z½[·nz\Í›7×õ༆³ßho¼¸¬P¡‚vÞ ê3‹ÙÕzºS»ˆDÇï5ÎyäÈ5ÓbŸþNƳ(·ïçòåËëÄÚF¢ÔÁƒëý2eʘ½aœ_ýµ~ NxñàŽ¹Âίq¸ru?Ooß¼•Es—ø»ìÚæeªR«²¹½ré*›uãÊ «R£’±)Î]”¦uZhg;þÎ[wj)÷¬•ýgvËèÉ#LI™©N—=™íì7šÖm)ÉUŽ˜93åÀÙ=2üÏ¡’)[F³ã7®ÝÐû½õ”™K§Éuß½¨Í[0.Cy‰ï‹ëz³§Î1í³feËÉëGeƒ—ú^ìÑÞì{Ûæí²pöbsŸ$\8~}\FÏq’ÀgB7©+6êi½ÞêAv⤹úÊ3eL­_´E ˆæú¥cc©P¾„Övw¦S v^‚|¯¬a1 Qï7íT Àè$`~EÁãz©X†ÿ×á´ÇôTLûET ,4  àC V¬XZ VC$$"KOœ8a£á‡£Ö¶Ø†„"C±X N H–ÄœÖcÁi»ñÏ5$UʤR£vkåÄ»­¶‘w‹#«U³¼ ÖÃÑ!‡e]:5Óù€ppçÎýR¬D-_õ»ÇÐyGruD¥Ÿ k=gÛ#~ï©s9á˜éjH@Ú×GðÌÌiÆÝ‘ÏÑ?Çõ—ò•«¿³z©RÍÇkß¶_ŸöZѾœû¶à47ŒaI8ÝçÌ™££Ù­÷ŸpÎC‰8r„©™ˆ#êrÌ‚ œÌH,}ðàAõ)ÌŸïó{Žþ mcœm‘n¼Œ„Œ ~ï 3ƒû_ÃU>eÊUù›dÉ’ésc–"âñ"‘óx "Œ †saÛ¿zÆy¬kãÚá¿mÛ64 ’¢>þ\¿HpQkíÛ›;w®–Ú±?æß>¢û]e笯€ºgý³üó ‰¤&µßZ ”,SBrçÏ¥‹¾+[RªgÏ”ìê=õ]g|¾ByÍ긟ٴÖSïãï©\•w2³½~écF¥š4\ªÕ®b¶K(¾äÊ—Sòg,¬¥›¦üñ—´ìÐL"G‰lÖ16âÄ‹#«·{˜ß;µê×2JK¦ÄÙôýžÅ%[i¯£OØY%=lXœx±¥äŽv£l»çNcSš¶i,y ø\/tåဿr銬]±^×Ù²a«àœ0|Yg¶R6ú @"@‡{ú08”OOšé·TT;†Äbé L;rä”J ÛJßä"bɘú©ÃkÔq{ J×9Ÿ-›æ ¦&wî:Øœ‚Ž1ㆠ[6Ÿ)²ö×€}DbaÓ†ipÐ÷ߥœêšƒrÄ¿|é<ÌH·cÇÉ;·ŽÞÉ™3§ÞÆ>hHÒH€H€‚0“ηN<^R#â’ ÖexÐõÏàdº{÷ž®'hp¶‚sÊ¡ý+u´²çDÞúDŸY¯ úç£Fô’âÅò[‹ýÝF"V8ôçÎ^F#$<‡38c†Ô*¡ZQí 6ŽÔ¹}vï\"S¦Î×Ú䇟Ð÷ ˆún¢îwÈj{v#œÜºA²Ò•«|¤ŒñÀ™ ÉFDê=Mzþê#yawu™›7Ìèô#Ãþ÷çAàC×ÎÍ´¦»µ_ÜŸAÚ3þšºÐæþ õ2ª ŽžÝ[©ö%­Í¸í€ÔY²d±9]w|GáÅ ¢Í ITª\ù]´*¾£`™Aòg«!:ýÂIzpªÁ©…—†cݨhokÄ7$[`K–,ÑA'F=¬ñw ;{ö¬î:é”~ýúIÿþýÕ‹¢”‚HóêÕ«K‘">0¸WêéŽí~`f4ëA_ @ÍßÝ%J¨ œ L »f¾vãÅ‹÷^Îvt„s»ÊÎ׉ÿ+¨ëpÖ?ËIÀäU([©ŒÌ™6OWDŒÕáî¹Þ')QH¢F‹ªëA²eõrùÛÔéRÙ8Ûuõ:ïuÕ’ £'égíÉL•ÝÛ‡Íu“ÖLg»Qˆd©i3¦•=;÷ê¢Ëj––;W9ñ ›8f²dË™U&N`é|sDz¾sæ[q“‚:܃ÔÇÁÁ|j£ÇNS2%žn cÓúY*ii ·Ú¸[rA\1nÑov9úƒÚu`¬ÍšÖVÓÔ‹¨h¸n²vÝV‰Ý\ÖUÛlGQð6î0£à1m7ÔŽ Q9ˆÂbt$ñ€b8àÓ¥KÇ(x×$@$ @rÖ¨N\þ úŽ+ë‚(ù‡'÷4$Y‚!sÈÑU¤Øô©>]È™ìÝwD9ÞoJj55ùOkÇ/ÛëµÌéaÈÂAÛûˆÒW½¨räÄTÛ©R%±I zöôf`õ¹zyDð°ºu*éÅiÇêÆÿóæ¼_UtNž!ƒº:¬ízà §âÎýÆ£ûGŒ¦´†ä#äò?褊ôƒ”Jƒ>yòD:Ù#:G¾¤JK ^ÄSR@Ðb‡]8ëx&‚>hù ˜™€Hô3g.Ê¥Ë×t4c<e˜*UR?¥#Ádäð^úåxQºûx1‘\;$IòÎéa9KŸµþç°(‘ã|H ‡»¡Kn°@¤ºaÏž=Ó›p¨92£õŒï)û$ªŽÚYÏi_Œd¬0#š‰_øá=ç>œõø^œ8q¢–3fŒ®ïj=]Ùò|àðž6mšÖ£Ç¬SD»c&;´ÑS¥JeiáxÓÊÍq ç¥î°sÖK@]‡³þYþyÀ=JŸ!½ü½ØÇ3*WU²2†Ã}å’U2hÔS6n…Ú7¬rÍJƦŽ87vâÅ+»wì1vmÖ-³úŽ9nsÌØI›!±i³ŽË'7 1 ÅûA½DX¥ÁáÅ™¥NÌá58«ï¨A dÀâ®!09…°ÐÜ'pæÌƒ†!ÿ„‘ÜÓHšj³¾ÈC49 zïÈ7$φÁyuìØ1½‹zˆ‚‡A‹³u¬2 Hº`Áõùg’öíÛ«—:ÉeÏž=R¿~}iÖ¬™ngüÀ, ¼€„ãŽ|œÒ2†þ;œÓØîÕ«—ÎѳgOŒâJ½1Þ9ߌóáû×Ò¨Q#A”8þ†1¶Ÿ~úIïâ\ÈñϬ܌º† îç­f8Ø2ð€¹ÂÎhc¿¨ë°ï—ûŸÐ_…–úMë¹}ÑpBCÖåºJÈ}y:¶í–¼ské'$L…Av¦Ô%̾O?mn#ɨ5ѨyÀnã¡Ê“æÈ I%ûcøÿñ¾IšÞƒ{ÉoJ;ßiø^:°ç ^Ð'úÎ?§´íÒZ  ¸ûˆ÷/Û‘€»¾p·ë“ À‡@dÛ‰£ë¥~½ªÚ•¯öxàÄ4sDzÕ©]QGB¼qãÆI:uôˆ£v£#ü£Gò¦aÆé)¿œÁTájժɈ#T2²ÝZsÒ¨Ï5 @ð'G¤ÔÑœ•+ûüÿþWò®àˆ?|ø¤^šµèáK²IÕ‘@Õ0CšÎØçš’œ»¸G´Ú€´ó(mÚ´~n@&Nò7oÞèäÍÖ>Ư‡BB5Æ Ã§OŸêûZ£.î]¡!?sæL}N”—,é#xHцHsh¾C®ºæp|Ç_©(t$K…ጚôp„¹ZÏ8u iœã—_~ÑÅø†Äc•*>ZÒV©/ãÝ:fk_öÛFÔ;^˜^¿~Ý<¼x±m‚EwØ¡Gãpç:Ìpƒˆþn*×xwoâ±x…îyçÖ]òðÁC½ ¶1+ÖˆóèjZÆ,ü]bʼn©ûúX?šµm,ûÎx ­.^HÂY¤zñrnçV/©òÝ‚¤®4náÜ>1Ž—BL¶×W ¬KÃC“&Mô‚s šgóæÍ¦¼ ’ª"É™‰–Œ(x$„µ×‚g¼3z,'  €%бýÏJp³ÜºuW¶lÝ-qâçRò~ÉUÄîWjfÛ5¹­¢ÿ «]«‚”+[ÜØåšœ\pPÃ)9sf¸±jÕ*}D‰ÃQæ—á%_¡B…døðá: t¾|ù´¬ "ÖaC‡5åÿýw)_¾¼Žbß»w¯’þI¢#Û‘£"a„ҰaCݦV­Z: *ê ykÅŠQàK—.Õ#:tD£‡^÷qþüy)V¬˜”+WNk¬{xxè—˜õ §6‚Op.ÿêé“Ûý@àËÖ­[õKDýãåÁ•+WdòäÉšáxG3¼€€ èA0+W®Ôxk¤¼«ìpGãpç:lGÉ=)Ó¤ ™ÓË‘ƒGåÖÍÛZ“}Õ2Ÿ¤¨pZç/œÏæDÉ,÷ƒ{ég[cö†µâéÞ²×kŸ.J6•dÍ™Åz8P¶ñ·9fè8Ý7¾¯Útn)ˆÂvšô©õR½nUÉœ$‡ž¥ƒÿó‡Tš<|rPÊ Ø) 0:Ü(»#ž0KÓ¦Mõ`º®§§§é„‡CÚ•Î QðóæÍÓ ê ’ÆÐ‚7ÖŒ‚wFå$@$@$à$õÜ´a¶ÎÕâ¹ÅKÉaÜ”×J–#q¢øzÉš5„Sz¶4ø OéB$…s:Mš4¾"Û‘´Ù™! 3.!Og3tßé¡£=äb.^¼¨sO@“ucÆô-èõ)S¦hG<$WàhÃË«ö;úK‘"…¬_¿^G‰£_HÉ šÝHªŠ:0WëùÔ¶ý‰—pþƒî™!“ƒsÀ¹fµtéÒi§ÿóçÏu„½1†ºuëZ«Ùlƒ8 Xæõëׂ>Ð/ú°7WÙ9‡«×a^î“@@¨¢’¢ÂáëÓµ¿Ü¼~SoW¨VN?êÿ~¤J›RÒgJ'G“sgÎËì©s¥VýÖ*òôé3©Y¾Ž\ºà“h|Áêw/òl*¾ÇÎêyذ'Ÿ›zïÉi¦ë(Èš3³–”±VŠ-ªDŽY^\}¡‹ ¹hØwUï†åΟë½tñö\“@` Ã=°È²_ `M \¸pþFÁŸ;wN'~rt¡ˆ‚ß±c‡^Œã˜Ž `#!kúôémtöŒz\“ ¸F Q¢xR/Qe×*³ "$!…\ʇ"ã![èŠ!÷„‘HÕ¯úˆVϘ1£_Uô18ø9ù­]­gmƒmŒÉ_D±öÇ­ûîÎÅKWú5Îá*;Gãpç:ŒóqM§OžJŠ˜i]?×H€:jÒp›:«W_;õÕrIû¼ö›Ç*רdnxÉÖwho)_ÌçX»&åüÙ Rºl)‰¦4Ýwmó’¹Óç™ÎvD–,ZÀhþÁ똱c˜}lݸU&Ž™,qãÇ‘d)’IŠÔÉ27óg.ÔušÖi!MÛ4–|…òJ‚Ä ä⹋2U9ä‘$?aä¼Ò™¿zùªÊßðVâ(g=Æ1RD£ª¹N¨$gî¼ñ‘š1 ¹AAîAðCáH€B$¬B<ÃèâÏ-»µöüí;÷dïÞ#ú<_ý•à%–#G›KßÞíuoïsM½z çèöáÇU Î÷J±’µåõëׂñÉ~ôèig\¯çƹ‚ÿß°Ñc¦I›v}$T¨P*ùyJ¹{÷.[ºl½¬^ñ—ÉÄàg.݉åGûŽý%|øp²g×LÉ™§‚ôí?Zú÷í`©ÁM    € ðE@tÂ>>-I“çJî|¥^ý’)ë÷*òèo?„ã³”Öõóä¯d>øÕÈÝsøÕ‘ |:FüÀeË–-òàÁÙ·oŸŒ=ZjÔ¨!‰'6ôòÍ›7:~ðàÁR¡BA|œ8q¤fÍš2fÌÙ¿¿¼zõÊQS–‘ 3Ç{Kï¾#uâíMf+Ç÷ ¹rq§ ÖC_ äI^¼xéðª g;¼—/ìý{<äÈÁÕÊyÝNI›ü#3f.ñÕîÐárúÄFÙ·{¹\¿â%1”Ü Óó®Ôu·mÛ£íaÂ|+Û·,Ðý]<·]~ÚM;Û}uèFÁ‘#§d›ç|Ýç9ï-2uÊPÝú·ãäôéózû}®iÒäyÒ²E]9u|ƒ¬Y9UfÍY¦¯©YÓÚú\¸ÖÛj§ú¶m{e§zÁ;wî’–‰)‚b·\ó»t~»tíÒ\®\¹.Í[öÔõ¬?ìÏe=†íßúw”…óþÐ\W,›¬#êíëpŸH€H€H€Hàà Ðáþá ?yÛT¤ŒaW¯ÞÐBƾ£õåË×õºqlÇÎ}Ʀӵ»çpÚ )ˆ¢Ëš5«´hÑBfÍš¥¦ðŸ—û÷Õ´ü¥K¥S§NR @AÂV¿ìÆ2{öliÙ²¥dË–M9W«éÿ¥sçβlÙ2¹uë–_ÍyŒH€H ˆ¯"É!OV¯n%)T0—%þo´h^G:wjª£ÛñîÈ2eL#ëÖLWNìyêÅì»| ˆb‡A¶ÅÞÚ·mhF¨G‹EJWHWä lêôEz]·N%É“'«ÞÆÍëêw³à=6ŠΣfoe1[BN¦ôw…õË)Sçëò÷¹¦âÅòÉÈá½$EŠ$ÚÑ6lÝ¢Òñ0Y³¦—çONÈËg§Ì1Œ7CÍx#íÚ4PÑí©ÌqõîÕF½ìŽ.[¶îöõ’Áþ\f£ÿ6 C“2e½y™È‘#ÚWá> @ ¤L@üÔ]4m\K0öÆÛR«fyI’$ŸCJœ8¾zpª¬#‹âÆ)h{ÿúãq  K R¤H¾´à8 “±B† ´à[·nÕ‹QQð… 2¥hÒ¥K'_ýµq˜k  HàÔ©szTY³¤·$NôëhSf¿ƒûј1£‰ÇŠ2{îr%~òÔYñövþÿ#yòD6Ý Ñ' ºñ°'Ïèunrˆ9²gD©¿¯•,‘ßWÓ¢Eòh œ3g.êcïsM•+}gÓo %Á3[E¹ƒCÁ"Õõ Œ½DñüÒ ~US23°%J—ÞKIGZíõë7z÷¬Š‚Ï’%yÈþ\æn |Tt¸TÜs2h°_<·Mž>}¦oÚ]9Ë”IƒU´MO˜ÉЉô«ÝûœÃ¯þxŒH ø0¢àHxŒüáÇâééi:á¡ÿøñc§uýúuHx"sçÎm³ÄˆÃi{  øø>òù^Õÿü@ö£;|ø¤Ò*¯¥´ØH²d‰$«r ×®YA=~"C‡M´¯®÷ý»'…<ìË/}?ÂNy]á=~8 X1˜"±)ì}®)A‚86£I”(ž–Í™¦¢õ×®Û¦œéeã¦z4d¼–ʆýã'>çDcÌ2°Z®œ>ù—"F o-ûsÙä ÀG#àûnõ£š' Hx@F¦;æný÷9‡;ãa] àCQðåË—× F gôÛx//¯÷Š‚/\¸°é„O›6-£àƒÏ¯GJ$ $WŽòýûšæÆ%¾}ûVš6ï!/_¾Hœ8rV÷ê=\;ÛÛ·k(Cu5šš OÍ76'НþÏ={Kõj?شܽÇ6 Üæ  ;ÇŸÑ2ÖªFD=f‡ÂÞ皌—F¿˜‘ ™F «k)üÿܳç°üÔ°£`FÁ¨1Så1}ì÷ªë¬_¯Š@ïÝjÈ•òÏ?ÿªÿ‘_Y‹urZ›î |Ôpÿ$ØyR YðBúíÐqG»¡¿dÉùå—_$þüZÛݯ«Fr3F™»ëÑc§ÉÝ»÷Íf—/_“é3ë}ȽÀ⚪×l) ç•_:Ô}âÿgΜ™¤JåÒz/1`û?'αIL‹®Q¢g–HQ3ÚŒW7úïr:uî:H/{>ðE„µ_n“ ¸F€î®qb-  7 DŽÙ& þŸþQ Þlµà/^¼è´×ׯ_Ë–-[ôbTŠ7®vÂr4Ђÿê+Û?£.×$@$@F v­ ‚äûö‘œy*h§0ôÌ—{lÐ÷êÑJK„9:Költd|³=eÿc1BxY³V}§«dŸ°cǽeŽÒvÿ±zYGÍ–•ù¾¨vD¯]·Ur竤^ô¦W‰¾Ê•+7tÕ;÷;lçJ!¤o²å,«ÇóêÕkÁK¼dH—.¥TªXJw×T£z9•,u¯Œ?K'ŽÍ–5ƒ\¹z]½ÀðIÌj8Þ‘— /À>{®rR±BI5cà¡,Ušî/^¼Ìˆ#šÃKCBÚÁCþÔÇ’$N 9rdrX…$@$@$@$@C€÷ÀáÊ^I€H€ì`Z=¢àHx~ðà/-ø'OžØµ|·{íÚ5Hxœí†óÝXGý]n‘ ¼7|oo\7SZµé-³f/“ßþ¡û‚¶9¤dÚ¶ià´ï߇v—ËW®«Ú{dà qº^æÌiÅcÙ$iШ³Ü¾}W;”Ýq¸£´ïÛ´Jfê©¢ÚÏ œàãÿè¯_Àá:ôû=Þ ÔE-^#ƒ7¯©Há<2wö(Sª% ®©ñÏ5TΓ§½ö Wé'„,O¿>íå»R…ôùùö­Ûö‘™³–J¿þctyõâ¢w¯¶Ò½[ ½Ï$@$@$@$@AÀûÝ‘½ëàˆH€H€‚!DÁW¨PA/>¢à¡ xCÞÝ(øxñâÙDÁC žQðÁð—ƒC&óç¯ÉCäÏqýµƒû«¯BKÒ¤ |åØ8|`•Íx£E‹"žçÊÅ‹WºåI’Ä—˜1}^ˆž9µIŽ=mj¿_8»Í¦­±Ó«gkÁbØ;÷äÖ­»Ò৪Úél”cÝ©Ë ½›,i"½v÷GìØ1dãúY)™«WoJš4É%R¤6ÝÄ5¡ÃŽ~–Ö­ê©\'Wµ,L¬XÑ5 8Ù­öS& V/úÉÉ“ç”ãÿ’’>6¹C$@$@$@$ðQÐáþQ0ó$$@$@®@4eöìÙõ=xØýû÷µ¬Œá€?tè<}úÔiwW¯^•™3gê•ï €Ëð]š)S—ë%Š'X¬Grž]ºt*_hã0×$@$@Ÿ€À—_~)uëTÒK@œ¾KçfÑ û   ‡÷‘^úØ”q‡‚‰Hõ:UƒÂPBôèpÑ//ŽH€>O®DÁC þÙ³gN]¹rEf̘¡Tb¼ST<@$@$@$@$@$`!`p^. |®ì£à‘hoÿþýb‚¿|ù²S<Ž¢à$H  4åhÒ¦MË(x§y€H€H€H€H€>/Q•„Ç/=;|^Í« ’NŸðÖ÷ 9¸8(:ÜCà‡ÊK" 🴀säÈ¡—Ö­[ëwïÞÕZð^^^Úï_<ôÖ(øo¾ùÆt¾çÎ[oÃÑO#      ø<Ðáþy|μJ   Qj¥J•ô‚êîFÁ¿|ùR6oÞ¬ãt &” ˜ŽxFÁd¸&      G€÷÷™òŠH€H€ˆ€_Qð†ÍáÇýÔ‚¿té£àèó`7$@$@$@$@$@$@$Ô ÐáÔ?!ŽH€H Hp¿oß>-x$\uf΢à­ZðiÒ¤¡¼3€,'      L€÷ üáph$@$@AŸ¢àsæÌ©—6mÚèß¹sG¶nÝj:á¡ÿü¹ólðˆ‚Ÿ>}º^дàóäÉ£ehråÊE-ø ÿkÀ†P7nÜVyö„Ыãe9"°qÓYºl­dÈZÖ¯.ÿûßÿUcY$pâÄÙxU¼$   OA€÷OAç$ Ñ¢GîK ÞÝ(øM›6 Ã%JäK þË/ùoÜàÃ5 $P¡¾ÒÝ-]¶N9_×d×ì+˜8vÜ[fÏYLFËa$¼H§‘ À‡à“ú‡Ðc[  p€£(øÛ·oÛDÁC Þ¯(ø‹/ DÂþýö[3kîܹõv”(Q\ « øG bÅŠ²fÍyøð¡Uy<xõꕜ;wμ¢Ø±cKäÈ‘Í}n„|¡C‡–&Mš„ü å’ *:Ü/;'  ÇbĈ!•+WÖ j¼}ûVŒ(x///-Gã—ü‹/t¼5 >qâÄ6QðЂg¼cþ,%¿¤M›VvìØáW Μ9#)R¤0¯¬wïÞÒ¨Q#sŸ$@$@$@$@$à :Ü]¡Ä:$@$@$Èà‡^;Ãì£à¡G»3»pá‚`™6mš®bŸ={v£ŸF$@$@$@$@$@$@$8èp®ì•H€H€>˜€_Qð»víÒQðW¯^uzGQðˆÞ4$h°f¼S|<@$@$@$@$@$B ¼yóF–Î_®¯.Eªä’1k†z¥¼¬OA€÷OAç$  ÷ `‚oÛ¶­îáÖ­[¾´àýŠ‚÷öö,F|˜0alðÙ²ecü{|6lB$@$@$@$@“ÀÒËåñÃÇ#Vt)õCÉyêq®—/^J³z-õµ4iÝèƒîü„ 4:܃ЇC9{öíö3èóùó²ný6:Ü?ƒÏš—²DüéÓ§ËÔ©S5û(xhÁG=dãÕ |6Â…'‡/ìÓ׋m $:Ü’fî+J”HròØú “çÏžKŒ˜®ü¼|ùRîݹ'qâÅ‘ÿýï»F9Ž»j{$å[C0ÒûÜ¿w_i) .\Ø÷íŠí‚8:܃øPÃ×YôèQª;ö @N†F$r à&ÚHœj\åÍ›7M-x///9tèàFÕ™ÙGÁ‡ Öì}C žQðÎ豜H€H€H€H€Þ@­ õäìé³rùâÝÁ‘G%Wš|z{Éúz]¡x½0¢¯DˆAÚ5í¨‚0OIšô©eËúØêåkäÏQ“dÇ–z?à;*VªˆtèÞN2gÏd–;¶ì’Á}†È‘ƒÇäé“§º8VœXÒ i=iØ¢CòáýG¤O×þr`ïAÝ&BÄ’#w6iÛ¥µ–Ã1ú†c½X-úŽ=ÚITåÃêÑáW9}Â[¿ ÀyòÊ#ýï#Q¢F1š™k ÿm”,[¸\ñ9'ÿüó r=}¦´Ò©WGÉ[0YÖóýX·š´îä£çÞ·ÛY¹d•$H”@æxÌÐc_¶ÀC¿`@;$Wí7´·äÊ—»âßç O?HÎ,™·Tîܾ«ÛáGòTɤI«Ÿ¥N£Zf7B:ÜCÆçÈ«   "+V,©Zµª^Б»QðÏž=“ 6èÅHªT©ð†sZðŒ‚7èpM$ð©@.ëöíÛ¾N÷î»`\³fuü´¤N—Jï×üéG©•ÜùsIÖYTôù69zø˜Ùf€Š`‡!ª}ÊÜ yX²IeÀˆ~²^EÅC*eÇ:ù¥g}¬Ÿ’fs?iò$2iÎxA=,~Âx2uÁ$É™:¯n3bÐh3e¤>fü@ðÏü•³µÞ;Êà˜îÑ¿«ÜR:ôóf,Ï [dŸ×~É–+«>pDÍY¬›V¯SUàÔ6,Cæôú…Bæ¤9t´{ÏŽ½eÓÞuÆa×½÷”¢% ëzxÁ©³é}ï“gôÚ¿ÏãüÙ‹º~¤NŸÊ|Ö¼8ìÞ±Gpÿ¡é€7p#Ø Ã=Ø~t8 |Z¸Γ'^Œ‘ܸqC¶mÛf:á>ì§<¢à±üõ—O„´àѧᄇ|´hÑŒî¹& &€{9rä={|p]ímЖF$@$@Á…@ª´)¥Vý6ÃEÙª­ËmʰsW%Ý¢Ùûô±þþǬ³s›—Þ†¾»ál7† F¶ñ”**Þš tËÆmºÊ÷J›Îv£ ­–,SBfN™-Kç/÷åp/^º˜él7Ú`ݸU#ípÇöž]û´Ã}¯ZösˆƦ¹FµÚUtDúÑCÇ”<Ís—Ÿ–ø¾˜Ù6’$K,ñÄDÙ?~üØæ˜³èÕV·R©ñSu)R¢dÍ™Uð‚ -ä Ã=ä}¦¼"  ød" ~ýúõ‚Å0hÁxFÁT¸&øˆTw×áÎèö!ζ$@$@Ÿ‚¢Õ­Npc ˆ#º¾å”½õûÜ{(•ÈS†Ê¨@o^¿ágb2¢@‘|´x­§QH݈EÄáqÞ¥´DA@ˆ0$I’„ð“&M¢'NpP¢Ã‡Óرc©Zµj­ø€ ,ø;w’‡‡•+Wޝf̘‘š5kFóæÍ£Ë—/“ug8 òäž D<J—.èw Zï¤A@ÂQ£þÈÕ¹wûžáÈu¤~Ñ¢Gåd’zô o0T\@0Ñ>ûÓûw¾Á@?}ò•lÁ½nýº`ÇUÛ4lOú˜Ù»6œðMÑÖ½¿ož«>רo—3«[çN›O3&ÌbvwÒdItcÿùógj\³™…Ó,ú‰#'sš”©R«Â ÉO…‹âãY“æÐ’ùË ?ÚÒ¨zS£œ>½83þØ{qâĦ‹ç.ÑÙ“çhÄÀѬ㮟iÍìÏ¡ôöÅ"ßDœ¦HKA@A ¢" Yð`ÂkÞ¬ñâÅõ˜Á‚×Lx”-Z4 hÁÇŽ[/{A@ˆà€}× A3fL€-E[KòÌ$7A@'#)kFºpö";ÁÓ&ÈDXzÔç Í ©æªT®Q‰VþÃøv+QwF‹®_¹ÁzíøÍƒ< ôÅÁÊ^»s5U¬Zžƒ{Âé½Q±Ül5uºTgºf¯W¨RŽj¸W7U®rYª\½"§Ÿ7}-÷\AiÒ§árµÃÎòšyôAª4®têØiÊâ’Ùõ/‹0°Ã=Ʋøm=e8U)YƒÀ¶ïÚº é=ŒemP?´†ºT«]…ñÇÞû¨×´.-]°œîܺK g/V8xQfÅÎ÷ö½ V{›«’(I"ªÓ°–3ª%e~%„áþ•€—Ç ‚€ ‚À—!<‚Nž<™Nž<É,x8àeÁøðYðC† ¡²eËràÕL™2QóæÍiþüùtåÊaÁÙ+’Ü‚@˜GÀ©GÒ„ù†JA@"Ä™¿p^#P(¤S±ÒŠ>vÚ(Š+&'‡cý̉³%Ê÷4qö8Z·ËO¶¬ì¿ÿqX/X9—†Oð X±c1£ýÊ¥«ìlÿþûï©kßN4cÑTŸï5‡FNÆÏúM9Åœí?þøµëÖ†–®_DÈom¥Ê— éžS(vœX„çhg;°®ÚºœÊV*c‘úè‡.ìc‡:ˆ:ïÞ¾#Ÿ —ÙÙ/~\?s ¡.Î4{ï#¶b¸¯ÞæEÕêTåI0ü+œí˜¯P2õ °ßµµìМ°=zðˆˆ4~‚øäš&¥rÖGÑIüí›·kJØðôƵ›'nJ•6•QWþ¹P«~ ª^·*]:ç£â1½¦Œ™Ó˜àö ³—Î`½Á,²[Ü0‹ìöîÂn³¯M*&‚€ ðjš aü ãñÃ?PöÜ?ÙÌ—0qBÂfÏà\ÆK¢´Ú±Å oóS®lAÉB:È«ô¤Ì!8 ÷U1sú®,GIaq¸‡á—Ñ«¶jõ‹&ƈÊ”.lqÍÞÉáçèÉÓ·«¸•"°ÅA@{@ÛÒšÿðáC <´à±ÔÓ–A£Òš=ztÃùG<´àcÅŠe+»\0ˆ&äºtéâ/¾/ôd]¬¶TIA@A Œ" ÷0úb"Cµê¸w°h&f.Ÿ<?«‚݉ÃÝŒ‰ ‚€ Ž@²dɨnݺ¼!uPYðïß¿§;vð†ü`Ág̘‘ðyóæå½°àŒ˜ 6À™››­ZµÊ¢‚¸&“gȉ ‚€ ‚€ˆÃÝ$Iè G:XïíÚ6 ð{÷õçl0ƒÜA@‚€€-üƒèÈ‘#†M`,øË—/¶yóæñ“­YðÐ…ŽS–“áµHRAÀ©@:ÆÚá.r2N…\ A@,ø^m2o"_K¯´×ÅðŒ€8ÜÃóÛ‹€u_îµ1P‡»×ÊͰåÒ$A@°Œ€‹‹ aöùógZðÏŸ?·Û„€XðZ,x°ãÅA ô(]º4%J”ˆž>}ÊÇ1®‰ ‚€ ‚@è ðí·ßRÝFµCçaòAÀɈÃÝÉKñACàØ±³tïÞ#J‘Âvð hê®]·=h…JjA@A „øþûï©P¡B¼é¢Á‚?|ø0?~œ™ð.\ ?þøCß¶ØC Þš#F Zð‚·€MN§!ià И1cø8Æ51A@A@A ¨ˆÃ=¨ˆIz§ 3g:sæ—½B±Üûônkó9;v¤wï>ð=s›‰MïÜy@wï=¤/^Q´h?R²¤‰)K–tô¯ýË”*àÃ7oÞÑé3鯿þ¦œ92ª5¯K»~ý?ûÕ«7?~Jåšœ\]]ômÙ ‚€ DÀ€wwwç M²fÁŸ8qÂ`ÏÚjò»wïhûöí¼á¾Y ^Xð¶“k‚@È" íp9™ÅVJA ²"ÿÁË_^EÖæK»Ãÿýïïa¨6¿*âpøï8\´°A½ª†Ã²2öîf9s{ôZ¹‰ÆOœg”mN—Y¸h ?‡®^½¥/û|ùrÇà.T¢xãš‚€ D,l±àïÞ½kèÀ;vŒ ÿ%,xhÁƒ/&_Ž@† ÿS0‹ ‚€ ÁEàÝ»÷œõöÛ”!q–à#ùGàáý‡!^¦èq¸ûÇD®|r)†{Ú´)鯻j‰ý 倸JY³Zt>}úL7íæÚÅ—J–ØY=yŠ'ué6ÔnkîßL]»£ÇOž±ãÜVB0â‹•t§K—®ù»ýàÁª]·=8ÙœÆékq°Ç$ò:ÅâšùÒ9å+6¥µkfR…òÅÍ·äXA #2eJÂ&< ,ø“'ONx°àŸ={f[,øL™2YHѤK—N´àí"(7€f{ÀøÈ]A@Ç€,Y”¢Ð§ß>9–AR ¡€>—ß|#®àP€šåÐ@YžáõÝ«ÒÀÁ8-XîÖw0Ïýõ7¾_§vEõ%a_WóñãgÎöªUÊP=w7Êûsv.cÕê-4Øc²’‡ù‹&NZ@ýû¶W Áèþê X™Ò…©Kçf”1Cjº|å&Íš½Œ6lÜÅ÷&(}áBy¨r¥’|þ›úA=fãϘQ}¨ZÕ2ôŸÿ|KgÏ]¦ƒ&°ŒÆ6íÐý;ÅÄ1b %‚€ D.À‚/\¸0oºåAeÁûøø¶¹sçrÐ}×4yóæeÆ®°à5º²F N:'»‚€ ‚€Dú#;ÛÓfHCG.p ‡$œ‹@³:-i£÷fJ”$‘s$¥3âp—B˜AÀ½neÃá)˜‘Ã{Z8¢Ír2õÝ«XïmÛ÷÷!ý²lÉ$úþûïŒký”ƒÎïuëwÐßÿMGŽž¡òåŠ÷ÍUÜJÓê•ÓÀYI“&¢R% R“f=hÉÒuœ•*–àú;~–´6Vš4)©{·FqÈëš2•(]ŸýpΟ?…²gÏd¤‘A@È€5 þÓ§OtêÔ)‡Yðoß¾¥mÛ¶ñ$¡/,øÈý™’Ö;Ž@¬X±O,)A@A@ˆÃÝ(réë €@¢Ð6‡Ü äZŽ9M ææÊ|øð‘µÕqé™\¹²*ù™;v+Z .Ú¾ußO¡´ÚÍÎvéÇ_õ!½}뫯f\0 óèf8Ûõe[=²7­\µ…~ÿýw%sNŸ¾H¹sgSYýf oÞ¼K³ç,§ÍëZ3gNGÏŸXêÁëre/‚€ X#%J,ø;wîx­ÿçŸZgåóÿýïÌ€·Ç‚ºÕÑ£û_ée³ÀP¾èííM;w¢ÿ*ù1A@" OtëÞzôèQš$íA@‡»|Â`®ÃწŒv¸C¾E³Æ!=˜ePÒ/ØÌöäÉsº¡àbºeë>Ú½ç°ù¶Íã)’RÆŒilÞK˜0-ò3íÜuˆïß¼uî`µgÍšžð¸Ñ¦]ÅÜŸ¨¤æ§|ysP~5©F;:Ùb‚€ ‚@ppuu%lõêÕãì`Ák-øãǶçÏŸÛ-Úšß$k|Ú´i-VšÙ-ÌÉ7V­ZE=vòS¤xA@BE‹‰Ã=ôa—' ‚€ NG@îN‡XjÕ¬@ºxXz«×l¥ÉÒ·ß~Kf9h±;b¯^½¡E‹½iÍÚmŠÙwƒ>~ôc´;’i’»$ 0iªTɉþq¸ßºuŸÓbéþæ ó©JõVtö¬_ûå—Wª ›xÃ…8qb1ë½{×;vLN#A@à"|‘"ExÓe…yµK—.ñ6gÎ.ÒЀ×zð_‹†>¬_ßvT·ve>–?‚€ „gNžº@M›÷¤¾ÞÂsS¤î‚€ ‚€ `q¸ÛE.}=àˆ.W¶mÚ¼‡à0{¬pÍ"ÏŸ?'¥TèÙƒ©p±Ú,M£Ó‚ñž.­+¥OŸŠ*–/NýާýŽëÛÁÚ›·þñçFÐj?}b#mßq€6nÚMûö£ë×ý$pжQ£gÒú ;éÈÁ5+V #¯‚€ !€5 þ·ß~ó§ þÍ›7Zð_›Ÿ(a|»«ÎB/)CÐBà…"㈅<7oÞ¤ƒR„ ©B… !ÿ€@J¼ÿ>íÞ½[ªbSÕª¯Ê¤8¹-‚€ އ{8~yµêõëUe‡;Ú·|ÅFzúô3Þq^¯®cìö–­ûÎvC3k%NœE戤˻ô¶îß÷[âîšÒÅ_’²eŠ6tâ—?‚€ ‚€³øá‡ü±àoß¾m¡–»=-x{,xÍ€×ZðÑ¢EsV¤\A@¯„ÀĉéÚµkT¿~}*T¨ÐWªEÀ=zô(5oÞ\É’ü*÷³gÏòó³fÍ*÷€_•ÜA Â# ÷ÿŠÃ_+U,¡·E£÷ï?´ÛïÞóuzCZ’3GÿÑGÚùsGQ‚ñüe{ýæ­¿kÖ>|ÂÁP¤ÕÚÞ½{O{ö5.§†¼Œ²mÛ÷+áE>.X0/–ŸcÆŒNn•KñÖ³÷H7~._ךõ|"A@PD UªT„ ˜5 Zð/^¼°[#°à·nÝÊa2;sæÌ† $iмÝFÈ A@@زe íÙ³‡ræÌfî6B‚€ ¡„€8ÜC hyŒã|ÿýwT½ZYò\¸Z ü?©Àoç8sÙ2…Yû<°’Þ½û`èµCO›µ]ºtΟ¿b}ÙæyÏÞ£hÛOúî»ï,î2‰~ýõ7¾æêêB»=|ø”{Lâc\+V4Ÿ¿:¤O—Šïã5óÞ¸!‚€ à0ùøñcúèp&I,~ÿýw:qâD ƒíýû÷4¿Áª ?„ þâÅ‹*pøEš={67Kë­µà…~>RSA@A@A@ÿ ZrI-„õëUñ÷ HÍ8bÐCO‘")'E µj5ÛÐÞ}GCï%ݼy—&Oñ¤b%Ý-Š:}æ±nË ó^´D]fÛC~×îCÔ QWš2u¡‘¼g÷Vôïÿ›Ï‹ÍË ?œ=z†ªÕhM+Wm¦GžÒ]%Qƒ@®Ð×VªdA}({A@&ãÆ£bÅŠ3·ds¬ *W®é šåCšÊ•+Û•' (¯Ü [hüôéÓU0ð³ê÷òíÛ·FŒA•*U¢xñü¯"3·àõë×Ì€0`•,Y’bÆŒIÙ²e£Ö­[Ó¢E‹èÆæär,‚€ `Lz9’råÊÅß½...ü[ Ýrm;w&777Zºt©¾dì‡Î÷–,YB{÷îåãñãÇÓ­[·¨Aƒ„ò2dÈ@½zõR¦œe£¼ .ðùŒ3øò-0¬t4hýôÓO\§dÉ’QÅŠYËœ¨?øÍ@Ø e¦ d hãú±cÇôe»{°ì«W¯NˆQ’YJE*‹C¡%qâÄyªZ½•Å=}R»VEjÒ¸†>¥4iRÒÔɃ©]‡| ²8ØlòÖswL—ÞV~¹&¾`ðòË/¿&Ùl­jœB 1@wdP gj‘"EhîܹԦM›©€”& ¾hÑ¢¼é ÁYƒÏ6ÈЀáþ×_éÛ{LÜX³àál0³àsçÎM‚·€MNA@ jÕªœÎ˜¸Ì’% ;¯7mÚÄ®7oÞLeÊ”açíäÉ“éÞ½{†\ ûüù3=š>|ø@}ûöe=ö7²lôÙ£D‰Â"8ª±=yò„à˜Ç¤é©S§”ܧ/9 åBj ×Ñÿ*Q¢;wŽób2õ?þà:nß¾P7LÔ£?0tèPv¼#߸¿Ö¨Q#–©Áä-â€dìØG?Î}ÔAKšM›6Úµkgdðà.\˜bĈa³M:áÔ©S  NÏ—/_Ò”)ShýúõŒiÆŒ9)ÚŒvÞ¹s‡'àì?þe†MñâÅj`ptýúu>û ³ðý4=q\»vmf®™ÔŒøøøÐ³gÏÔ¤TdÀ¡fËL(°^o޼Ƀ 9rP½zõøØVžà\ÃÀf÷îÝtõêUŠ5*ëN£þ×Ð> „áPÄÀ í†#Ì­²eËÒ¼yóèäÉ“,}Ó¡CúæËŸaà{úôin;º`‰ÕªU‹Ò§OoQ¥3gΨï–3,uím|ßyŒg_ DíI¹\¾|™Ö®]«VÝ<â4(L-`ݳgO‹ç˜O0PÇJ0°à Ì0ÐÍž=;3ñš5kFÿùÏË"÷Ã1©S§&l`HÂà„ƒÆì„Ǥ˜=Cÿ;Ø`èÀùÏb‚€ Dvtßè/à·'<úrø†ÃNl0¥1±yåÊÒNãmÛ¶±³=]ºtôóÏ?³Sù1I:jÔ(fµcŒŠÕKýû÷'///Z°`U©R…7¬PBo̘1Ô²eKdåþœíÑ£GçþTâĉù:˜ô({8ÜQg0îᇳ~ÊKš4)yzzò¹½?è»À gàÎ;©¨šôE]á‡ÃÌó¦M›Ùáp¨M(L{äÃäÜøÍ¡Þ¨?øèÂz÷îÍÎvôç–/_Îý'L /‹wzskC9`ÿcåÚŠßIزe˸Œg`²†~~ó°¢ü ðuù#85+¼iÖdߨre+•W÷ÏŸ>SÛÆžÖZˆÃÝ™);XXŽôƒU„d²FàéÓÔ¸iwºäsÝ®CÝ:³Ïß¾}ǺæQ£ß‘Òuüû;9zdoÂfÏÒ¦u¥€Êh×¶!5kZ[u6¯ä_Ø@IDATÒ½û)Aü¸Êiåj@õÖõ}`Zñ¹rúv²¬Ÿ/^Z¼pMž8ˆNœ<¯–ÒðuÖ§sõçh7ç­Z¥ a»wïÝWÏòô9kл$K¬êá§ánÎ#Ç‚@h"€}“&Mxçbð§y·nÝØé §:–»jJ/Æ@gÍš5¼d5mLÝ¿ßøß@z<Îs8€!MA–àâú¬Y³üuìà\dž%Ã>äzÁYm0Ô“wïÞå \Óƒ%Ã0°Ã Ælȇä1ÀÀÅ$XÁ$.оà–5ƒÙ U0ÅP>ž‡6ö—Æo,ãÖ†-&ଳƒW,µ6l¤+T¨ “2CmõêÕ|Žg`0‡ò´aÀ fyùòåõ%Þƒ¡§z„ ™I†‰°»4?{‹/fæ–Z;j˜ÀÀ và"yÀçÿGæÿ%3 ŽxLzéÏž52ø?€|–0Àý¡Ã§òj·dêwTLȆúQ`=ãû¿Ý;vd¶5úcèwhF4œ»èsìÚµ‹ÃèGÀV®\Éû† ò^ÿAŸ G|Š20q §3ÊD?$…’Z±g˜àÇsàü×Îv¤E_†Im¸¿páBî;¢|´ÌrôÓÀðÈÐ_DzLàëßÔµ}ûö,ÿâ9À·#mB?ùºvíjÑD_ýô9Afž«V­âêANGŸòy˜È0·]·« ÐNàÒ ÊѦûZ ~:tˆejŒDL$X2t>Ù ‚€ „|½᧾ᢦ£ÆÌT:߇UãfO¿}ûžÝŸ?ÿ—;._£øá†~xd3°âóäù‰jÕ¬ –2þlálª)[¦U«ZVu‚’íH 8Ìu§6ÀLê&ôäñìºu*SéR…ÄÙ`r?TóúÚ`•b–´:1¸ûlw­Ý©+täÈ^ŽŒ%¿@yƒ`ÂX.Œs ¢à”×ÿ¸†g`æ–é‚m…å¸p,c0g¬µAÏŒ( œÀ>‚Ž&X€™­ ƒ‘Ì™3³ãËta¨´5õSkÃ@ L$<K¦ÁLB±¬ÁWÁ>Á1°ÊPgØÝpäƒå‹A+œí¨¡]¨'ìfã¬OŸ><Éç?è˜ð@;¡¥j6 üаÁ³0à;~æÌ™ü<è§‚E¦íÓ§O<ÑA8ØóÀNO0î5ãJ§µµGÝaÐ9uÔ°D翘  ðø, =\HM‰Ïkܸqéùó—´ÀÓw¢)À„rSˆ&âuNô °Þ:&´ñ{¯Üh:H °+VðŽ\H΀´ W"ñ õdMfÀ5³cØÞ¤¨Î bú4 J O=v¬œÓõÔéôäL`ÕœÝ `E\`†þ Ni³Áaß°û!ó¢Í‘6aÒ†>*ú+z£uƒa¢ýDŒ¥áÄ·v¬£O…gkÆ?gRP_ÝÇEŸúûï¿×·x‰ô]Q6Úç}©R¥hÒ¤IÜÿ6¿‹Œr"‚€ n†»^ÕwPê—‰X¦Li¿¬É-8váì‚“Y;EÑ8hl‚mƒÁ“yà¦;H€½ Ö³– ³ÙlXZ ‡>XX¼hCp(,Ÿ…t–ƒý+V,}›XI 8¯ÁÇÀdþüùÌv­QÃ7fûdÁºtéÂ’5`i‡?ß°ú¦–c09{ölƒÍ6°ûÁŸ0az¬²;tª(#PÇbµØä­ZµbüÀÎó YNsl`pa’òL™2± ÞkTDçËì6mx€`÷ãyX† ƒö*ØöpiÆ+ 0P³_3®tYzz¢ 11à¨a;ž‡Ï «æç:Z†¤‹¸àó†`Çæ€Ç±à3g–¾LÄýDHËA 00ýLÚƒˆ§1~c1±m–'ãR) 9`ò«ѯ‚T äêÌö%¿ÍX…©* i‡>úècbU-ÃÊDm z˜ûBúºõ„X`LxÏ‘6iMzäAÅlZ:ðA}6ÊAŸ ý+ôÛ°±l 3¨ ¬w8ÛÑ÷ÂêH¤Aÿú‡è—YË ê¼²Â"O=¡x âÙ?Ú«/þOÜ{HI’%æñž9&å^)iàÄIyüðË‹—ü?_ÕÇQ{ÿî=ç‰3F YP·Š\ûí·ßP‚D ,&,Í, "âpwòëŽ3:³§¿ùæßÌR4ïá,áóã»W,F›÷­®ƒíˆtŽä¿§¾ÀÊWjªÆD“/'¿k)^O€Ùœ$I g»®?bèð놾nÞc 14,bà€9ï+h·chmX~ &7˜®Z+Îf8áá¿gÒm†ã†ô_bÐÇÄÀ,%[zåzð…%Ä!ip,3ÎÑVtØ´“ƒæÁj’+à‡aÀˆû¨3êf‹Œ¥ÐÖ†A/&9Àlà ùÀ„ƒcƒt`V&:ð9€cßÖ;Òå‚ñ–>ÒÕÀÀ“ÐiB,xÍ„G:¬´ûËñ[µt§êÕÊ”]î ‚€ a€Ãý |GêUnè«ádH¤ 8å]À¨®[·.¯zC_à¡’çƒYËÉ|)Xx.úê3;ØAj°eXá„~Èè¡?Š`ªèÿdè×`œÔfCßÎl”…ŒA1”‰¾*´ßÛ¶mk‘ñ„ÐOÑÁ0LZà9f¶:øC¤ m:@*ú> S`U!V'". $ ôíZ´hÁ¤8üQ¬L@l!¼S½²P—){A ´Ü{(m߸ƒ[¿©;µïÞ–&žJ+­¤˜±cÒ†=ÞÔ­MOÚ±y½}ó–'˜2ª8ym»´¦ªµÜ,ª;´ßÚ²n+¥JãJ ×Ì'œÏ›¶€Ç< WÏ£ UÊóñÄ‘ShÚtëúmþŒªbêeù)õÔƒ ÉoQ¦õÉ‘G©_×tù¢¯4iÂÄ ©`Ñü4|‚ÅŽÛ:9?oìÐñ´Ñ{ ݾá»B9}¦tTºB)õ¼îD'dÞµmM?“ެÀÓ¦ûxš®¯C×ÝÚ°RŽyô°jå@bu/]º4ËZçÑç‹v žœãze$H¸&:¤ ƒb(úñ˜@¸ÂÐ&°ÓáÌÇDäi°aÕ%ðĪJÆÙè·"NÊ› «ûðn°à¡ƒ°¢¯‡{5D,%ôù&OžÌ˜ ïÙ>ã=B®+@r:Íž³Üxby-Ÿjœ‡¥ƒ³ç|¨Ž{®RN51 ÷°ôvl×Nm¬NÃï6ÈnnnìˆÕ+ãp߬¿ŽþúOøí‡AꓘÁ5=i>fÌ–²nyîܹÙ! '8VÂñ> ¤b`x6利=8Ù‘VK‰á 9ôËΟ?ÏíáŒVPw0øÑOÒŽiL:€aÓÞ–¢U1§x&dQ&Ú:¢/¶~ýz^ŠÉ=á 2V‚ÅŽôx`¶ÃÙíøæÍ›[”m>t"úy`Êc5!˜îÀwô¿Gï Î}LDÀ ãå@2Œ /ˆÃÝŒª;!}†Îv°ÚÍŽsýÜï?ÐÁ½‡¨ÿ°>Ô´Mcú1êtæÄYjݰËÄL7ƒÊU*CyòçÖYxÿøávh§L•‚µh@I“'U ö,4¨§‡ál‡s,ù¸ñâÏ…ËÔ±yf­ƒý^¸x!ÅZ/`Q&NÎ:O`§Ï\42eÍHo^¿¡éfÑ”1ÓèуÇÌ|_½m…‘ohßìlÇÄÁø™c¨bÕòìL_·rõh×›ü}» ¤Y‹§qž9Sçñ}0ô×l÷¢¤.I Œ>‘f¨çÀ¹4ÃÆm¥Q!9ˆü+B¶ê+7ÊÌpÿÊU‘ǨêÇ"³bÑaK•J–BÉ@G,o ¬ Ë“€ í_b`+ÁÙŠÁ„-Ã2[OOONƒû*…ô¶&*[&ŒL,åõÚ€ÇÁ2l4Ð!ÇàÎÌîæ›êœÒ(šƒê{ÎÜc°³n;gK—. ðÑÐãÞlÐÝ·oA+^cƒ –‚ Z0À0Q‡¼ †à¹¶,mZ_Ýl]O[iì]Ã{†&&Dd† ŸFÝ{Žðçl·nóú ;©Xɺ*0òSë[rn‚EjRî¼n¼=zäû}i'éW»üéÓgµ²a“Åó×oØ¥˜÷¾ÚÓ7äD``#@:œí`=Cë»_¿~Ln@Ÿ ŽYk3“¾TNv . ð:Ûƒ#ñúò`Û£â¤ì<Nm°¼/^¼H)”v9ê­ ùAÀJº€¤íÀ®ß³g5nܘ°r1|àðÆJJÄæÑ¬s]®#{ô¹P&0‚ãdȸ Ïò&´U®\™±Ç„&ÀXG<Å;ÑŒ{Þ¼‡–ØAáDGŒ”æ:ðÀ8Á±ÉF¬ô¾ÃŒ"8Ëa=¨ØV&–ºu}·jHzu`¶;þGsçËE«·y« Ç ›h…Ô`šï9¹ƒYón5*±sÜ{ÅZN[§a-œûpʃ­ÆÊÅ3À®‡e‚96‚!]S8šÁÆ4:= çtP Žo Ä`(K’€ƒ:”‹ÀXX¬ Ît´ƒgà‰za 8:}pü# šfìcp ŒQ¦Ù0Iü L,`†IL,`™µ6ÍN ËÒ±¬:°ã]£Žö¤t€Î`_a‰µ#+ð\¤…ÃJ-k£ë#{A "!°wßQ8x‚ѤʕJRßÞm•®oFã=íÙ{”vï9B‹—¬e¦ö3lË­ZK:yl=_å XÔPÚþ¹sùJ:$w z¬‰`=Ô*ÓÚuÛ•£ñƒÅULú.[¾:´odq]Nà"€ pômàà†ãý°­õäºu¹z"Žb/G§A_Ç:ཾg‹@¶<ý G@¾F?ý9Ôr…¨ ú 00Ð! ˆkˆcî7êç€qƾ#†Õv j Ÿ Y@8Ç0™oîµM(R4Xa ít8øÐ?4ë´ëº¡ÿ¦Û =wLè¶ê4è‹ÙÂØ[_‡M§Nx²ý\`¬ô;C™è¿ÂÅg#0jðXfiã9p„÷Ø-ÀG‚¡nm®©S²úöM;TÇW¶Ì: ôÎá¬ÖvêØi}H-;ø_-3VLv„ÏŸáI—ÎûðwõÊÙRåKR¢$‰ŒrôA«Ž-r0°“ê9¹òæTò/‡ùÿÿïn5+餯¾†{5òè;œ‰X›¼7SÇží)šLØ»s?¡®¥ó—§šîÕ)_¡¼ìd×r;Fr üƒ€8ÜðQ° rî„'H‘‚€ |pHÃ) ÝL°‹àÔÖG7œååË—×—8È&t&á<ÐGº6ä±v¸c ‡1ØW*1³ÆÁàAypŽk™”Vt-ÁÐÆ†dÙ²e™õÓ¡C´…I0ÕádÖ†åŘÀÀé``‚!/˜Kp¶kCùpþ"˜îØ`èta@†–úÇàd» ðÂÒdèÔc¢÷0`àm#«¨b¡#($ž‹÷Æ: íù,½F^ .\È÷õ¼»%K–ð  Pè¬Ã‰Æ—y < KŸ1) ÙV¸†gÁ9ož Àu³AOËÖñNõRpó}[Ç`Ùc’F˜Z¶Ð‘k í;ÍÉž=­]3Ë`xÅ—êÖ©Ì4½ÛuÈiÏ)÷ ®ªI´ÌF^9½z¶^ÆÌ5Á*£´ŽÓ”© ù|ÁÂUâp7‘ƒBìZîÄV™:غþ­G ýœ0kGÊL¡˜ëØÌg6H!mè_"HiHÊÌ–-›CE¢Ï„-$ ÏE?ØÜ6— YAL ?(&8H¢hC Th®CBÅž¹¦IióVÆ,é ÷_?þJ¿¼xIñT?Èl¹•ÓÛl7®ÝäSL6AÆ–eÌ’Á¸|óÚ-Ê–Ãw’]_´W—tÓò$&¼îÞöe©_¿rƒ³´”6~&]„Íýk%MƒìLÕR5éÞû¬S­zØ?þ@‹¤æíšPÑ’Eøšü4âp×H„àÞ<{mž¡ÁGHQ‚€ |1`ÁÉ f8¤XÞ½{Çl8qâø+VÍöw3€ `6c}p0˜ð 8|í ÁÜ‚(ÒbI2œêš¹¤u:í=ŒmlX¾ ö‚‰ÙcbƒQç5˜ím»ì/[Œ&{ϳu½V­Z„ÍÚô‚¾Ž¥ÉæÉ \‡¼&!°D9Ô_[@ØÃqw˜ìÚ‡¶‚‘…ß&°´À¨Bð®dÉ’ʲÅ$ î`[9êpDzi–¡‹ |mÓÚžc«y³ÚÔ­Çp5åáØñ³:Üï߬‚¯úK²ÄÌ–ÇdiPìâÅ«tãæ]Š7ŽÒ=þI}Ç}g‘N98ýoßy@)’'Q|iU¼…(i¬OíÒ¥ëôì9¾k?QâD Ôwujõ]âÿ·Ã:¯ùßkGŽžQò ¿QňK›6¥]ÜÌù¾ôøÁƒÇtNiÂóÍ¿U Ûäj¥Or»¿Ž>ëŽÂoÿãœ<®Zn>jD/–—–?ð={Ö'À÷lë9÷î=¢S§/*|ã+|Ó(É _é4[iõ5ÔãŠýüŠ¢Eû‘’%MÌš÷ö>:_XÛCžR/_½¡|y³ù³…ü'NžW«²žRñbùÔïi°ÖD§Öý 0ÇáLÂÿ8úè_ˆ…?@r€4£Öt-‡'ð[ѪS š9q6f:µèJ÷®µûÛl¯OòÝwßÍþ¤ú fÃÄš‰þçr<ßÞï•yL§Ó›Ëýö[Û®MŒ“«rt]tàgÔ?çÏÙÍÅø;Ž?_ƒfûQŸƒ´aõFÚµm/8r’?|Ì}˜›w6Hð¶*ÀßäB„FÀö§2B79t'÷ÐÅ[ž&AGX3è¹ËŽŽÖ,¾7¿DïdG ¬o0·ÃŠa‚ì}g™W8Þ±9j `é›eË–±vªž±—þuëÖq ²àÈóØ+W® a¿þúÛ¨Ö¦Í{húT›“Xø!ˆ¦ÖõÎ>µ‘O€¸1rÔ š¬Òæà«`Q!°ûì™Ãý9-ö¦&Ízp]»4§–Íë{ƒÎìèÕåâÙíÛ5¤ñcûñRjèÍÏ›¿RÅÏø¨“¨ÉиÊYÜ“7ªa\ÓÏ•ƒA_W®Úb‘÷1h­X¡8Õ[ýžX~¯X×mè®\× w©‰\¿USX0mò%Ãå+›…Uå+6Ñ7ö©ÓåãØ±cÒËçgùúîASË7Òãí¾ýÇÒÏÕôæÍ;‹{pÆöèÖ’Zµ¬kLðZ$pàdábßÉE$­Y£ŒzôÓ “Xi mùŠúÐb˜zâ"iÒDT´H^‹ûÁ9Y½f+åú¹29rÚÂÙŽ²n*GJûŽƒ¨mûÄ^ù˜¤©^³ál·—.¢_‡“ëÈ‘#ê;à,Ëóae¡˜ !PºB)N2pD?ZŠ“áýGC­ó/]°Ìú}T22›×måë"joΘ-Gãtñ¼¥Æ±>ÀЏUÿè°§P+ӢLj®o{Hà¼PÄkóZì«ßŽëY~ò•ÉœÍW–~»[v[gaø:ë6h¶#pj‰~¦‘>±ZE†Éˆ.½;×nßp,&…‘A"4âpwò뵞½sòã¤ø0ˆ‚¥Í_°’—]‡ÁêI•AÀ>|`m{h¤;ºap]|8ØaÐ|‡6kãÆYkߙχ+4ÿ½½ýœKöž·víZ–‚¾¿˜ ÑèÞµ…óx×îÃì<̘¥uì<˜6mÞí/ ¦-LzôI;vä[wY¿vÝ¿s˜XMîuÝø:úp~¿|ùÚVìÌþðáWš2iݽuH ·[0|ÁR_¾bÕ®U‘Nß@î!ïÕ3U å˜\ `K› a¯]»Í—âĉEcG÷¡Ó'6Ò³Ç' ìf­CÇüì¹ËÍY-ŽçÌ]Až þýÚÓ…³[9h,ŠF‰â»üC‡Oå<9•¶ýÆõsyûî»ÿåÌ™5‚¯-_ê¸Û¸iã M»*˜´¯Ö*Øì‹ާ{·ѾÝ+˜õ ö8 AOáÔª!.œú0°ÉóçÏÉÇØ'û‡Ù‡¾ ÊÌ0’>}*Ú²i¿»¹³GªUOYl“&/P¨Ÿç8îÒm¨q^µJZ³j=ºŒ®_ÙCXM '-P²nï´öš4ªiÜZ±Ò¶Ã}åªÍFšúõª°@®œYiâø6·Z5+éqP®lQ£^8¿­X’õváI%°³{÷jCgNn¢;7’çü±†¤Ì¬ÙËx2 ylYÏÞ£ØYŸ8qª£&<Àˆ‡¬Ž˜ ‚€ã€¡=|¢ïo VÊtjÑÅ_Ð_”6oº'-ó\aüîí;jT½)}ø'€¸=f¼‘ៃEòSáâ…ølÖ¤9´dþ2ãy`Ž£LíLïãÑË:;Ÿ#fTãšÍŒt¸öùÄ‘¾}…”©R«Â°ÏšÝ×Éß¿Û ºpÆÄðèÁ#jݰíÙ±Oý£,Ù3“KŠdtûÆmºxî’b³S¿¯7¸üÁ„ø©ãgø«3fõÓš7ÉA¤Eà›HÛr'6\îN7ÁP‹V}¨l™"ê¢†Ãæ}q•1c˜4Å?D ‚ÀÇÙ‰”¬ºpá d VZb…ƽÙà4B XgÓ¶oß>ÐÇlذÁ¡hI „cR¦LF»w,¥ÊU[r1hœÔئM_Ìÿ¹sg%·J¥¨Mëz=z4‹ƒáí¹Ð—‰§+œáZsŽ[8p£Fýà´S|¢r¾ÚÝ¢ }G-µjƒƒ|·/ã ײ¨€f+–MÑ· ,e8Ëu@×›V,õ­ÛöiáÄmݪžq^\ÉÐxΣ˜ùÊv8xÒ¸g}€>ò䉕´M#ãÊQ£þÈ2:¸xñÒ5¾MøŠJð±vã¤x±üäêêÂ×ûsPÕeËÖ½œ ’c õìÞʘ Òé°G}áÜŸ?w4²3ß“cA@ǨR³2­\¼ŠvoßKÇ ù3ýÆPŠT… G°¾?eÚBƒÑ§¶v¶ëûØÃÁ®û–p˜×pæj«£ØíÖf–ÅÑŒm¤#NÐí[ñÖBéÃ[õÚÀæ¶güÙ¦u}·‹Ík\»{÷‘qü¥3gû-MÇ*Í87—ÛW9ØÁ ¯âVšR§JnSszó1´ø7lô[Ž®ådt÷:¾«p¾wßQ¬:à¶uQ:ëÖAkY¶l~Œ9³¶{ü¹Œ÷²sÛb›ŸGß®s"¤$mÖ,wôgуåÎͦ“]çÅÄ[µÆjŒ âÒæ ó”žxT„1,sætÎv,-[ÔåS”‰Ï¾-³ŒxL®ˆ ‚€ |c¦2~“†öÎúêæ—®[He+•¡×¯^“Ï…Ëìl¨vƒš´f»W’CÇýÐ…}ìPG`Ë£Lô⩉ýñ3ÇÐ|/ß lsôqÃæõUÀÒîóŽíl‡Ó~ÕÖå\OûÜùrѳ{¨P±‚¼J ÚñÚÙž#OvZ½m•©XÚÈÒ¢}3š0k¬Ò÷ðôà1VÌv8Û]Uà÷± «>Cl³ïBä Ò! w'¼ra¸;ÔpXdr5ëù÷ßÿc֪ߥÛ0ªT±%P?‘ÑðÁ&tN±¬úÓ§Ï CHè~FF<¥Í‚€ a°ÖÛµmÈžîÛœöì=ÂÍNl8¥«ÕhCözQÞ¼Ù¹!—|® ‚SZÖ¶ Žó‡Ÿ°Ó\8)Í–!ƒ–1îÃé®ë,™Ÿó]ç5;)ÁnÖfXéR¾K½õ5ü†C«ý†ÒÖ>á Ížã·¤\§±µ‡LޭɏD ãÉ1¸)»vý¶Q”Æj\øç9Ô Ž-[¾—“#/ÞØÛfƒ£¬o-Çã¹h5 ÔÅœÄâ8kVËüú¦=|2dHMØÌ†à¥x/W¯ÞR“:ûh÷žÃæÛ7i\ƒã ±×ÊÍ4zdoc¢Asµ5¬oŸ½ˆ~‚¨^¼è»b²A›ÖÏó7éá£ØÚ\ÔJ{ŸûX¦ t.^ÕY,ö-›×5ä‰,nȉ ‚€M†ŽBØlœÕ÷ßùýŽZ§I ~»—¬õdVúUŸ«jµZTʪôصT›9ýÔù“[@+v,š½tÇ긥~¿Ÿ?{Né2¦£„‰ØÌMõ¹~ùã‰Å½öÝÛ°D  ™ë=QÿýR1kw®âÓ×TìãÇ除.Iu‹}ƒfõȽqzpï!Az¿sI’%±äÕ"£œDZÄáî„Wov¸k’¡Š„#AŸ ÊCyÎNýŽ£+Wnñ ¥½¸by(lÍ›¿’0x*Q<¿Ò#-GÊ·Àa×îCÌzAGüýû¦ ‚“õêÑÊXº vÙÈÑ3Z“n•K*-È j¹­uƒgÔ™âÆm”‹¥³´å­œÄX‹ÃO*ИR… ç1ÒYTË_1°Ìžë¼Zû;%JæÍe“}d7"C—sÑoZ´x-;"RÛ¤-‚€ ‚€=À4GÿEëWŸ?…¦N_dÈÆ€9Žà¨Ð@GßRm5j9&k¢Wê|Øÿà€´”ÖL7çû÷¿^øºÿÀqZ๊«‰ô‡Ìý]s9GµÃVŽÜÌÜ¿ŸvïÞ­äŠbSÕªö'“ÌyäXˆàxÇR9—ô™ÒñÔ2±â7oÁŸƒ” y²ç¶ bo¯à”Ç&&†€8ÜCHî‡ >UR+{è®bjað™6­+/aFÿر³ÌγKJÿõ¯ÿãXÞ¬YWpA°)¹pv_R: #GÍP ³cJ·Ó›ï=Uz^xÖ5¥ó8iÊfì¤RKˆ‘:•›·ì¥ý{VôXa` ¡ó#F4f.Á¼yËÅVÛO›7Χ2¥ s:[ºõÎËfO[J?ç¯ÊÀìé­ÚÊ^¯aFyõš­´pñ:tèT€Í΀=Àå¦ ‚€ Š8p‚Μ½ÄOÄ„½YÚ\Ÿ~ÊÈÚÒþù'-YºŽo¡‚X&p^þõ—«i¡1VlˆÇd2t²Q˜1£ØØ©”æi>ÅЇs¶zÍ6Æý°p¦üï¿ÿaT%Z´ã8@ŸñÂ?¦5&!°dX™€ «ºïPZGî=PKÚ «Mø1üàtN§úÑ`ÖWTÄô«1Y;±ž»M™º³-÷ÚÈwô‡µŒQ…òÅø½Û*$™aÃý$GèÅZô¶ÒšW4ÄW«@“&Mh+™Å5jlY2“l’­ûrM·nÝ¢±cDz,ߘ1c Pø¾yóæê»-«8Ü Tä@A ¸ˆÃ=¸Èïô?㮳;Æ"ØZÊ3kÆpÖi‹¦Lù†ì°…³}é≬IŠeÎÅK¹óu8¾õ eôØÙŒF·®Íiìè¾| LºŒ%X3õÆ;ìÈ×ÝTËmÁ:ã]èp–­Ð˜NŸ¾HMàç!€xׯì! `Õj´¦õvÒ¸ñst¸ÞƒÞ¾ýÀù6o˜ÏŒ{ýüˆ¶‡ãüÀÁJ2f ̶ŠD´6J{A@k®«þ‚> ¿ G¥-Ù+ï´Ã¿¯_¿S«àâSêÔÉÕê>_¶ïj¯é2@X0¬ ÔÎöï¾û-^8ªW+k1!‚DX3¼ƒT©\8&ê§´y£¹¾˜4ùû¾ùòæph5â|Åö×9žØ±cêSû—/_Rz²ªû®þñBËÖ} g;‚¡Î™5Â__3¸7MÕ4îè×!6ÁÊU›6jXÝ86œ=ëCMšõ0.!ðkOµÒÔž¥M›Ò¸UªdAî—‚xRŽ˜ Ïž=£9sæ¨ïÝDdv¸'K–ŒêׯO...!÷A@@‡{ }Yq¸ ?0VtP$ +]²;Ö0 À€'P¥Jø^ôè™ñ€ ãúñ1JÚ°X,w$?ò:’Î^É/øÙûl8r]>?¡óùÛX;GÍïEðü͘›Íø_½êÇ"Æõ°fyö[†üÛoŸ˜M\¢x»Õ|÷Î7à$ ßg; ¬díp?~âœ]‡;Vô”›¤g$d´5nTƒjÖ(¯OýkEp‹–>]*ÃáŽÕ9rdöWÍ»QÙòø:ú‚ÏŸ>yü—¯Ø`”5~l?£ÿj\4Lœ4Ÿ°ê† «jÁª€/1PŽªU ÚæÏeô9õ5ì_¿yk>uøúóÙ³gâÕŸ¨/ˆ&X CÝË—+ÊÇæ? ª¸UkiL./–ŸfLó0'ñwŒÏ½¶“§.pÐ=[“øß8vÜ·½™•ÔÀϦÿ;_ö‚À— +W.Z²Äv@Þ/)Wò  jµ«RÆ,¹Iß+©]1A@°€8Üícì;ÖÆ` 3¦Ha B³…²ÿ”‰í’(Q¾ãC3Ö%K䥽3g-cwä¥ôRõÒWWïÁr²xåÊ••%` Ã 阬Y3P¯ž­iô˜Y4|ÄtÞÒ¥s¥BJŸº•HDFûõ×ßÈ{ívf³u™²5^X™ W'Xß ©óyóæ61A@A ¤À„:ú`1Ãê5èÂÒtô°68$‡Ÿj\FB[€rÝú|:lÄ4g¦” @f)ƒ2sÖRj×a §AP°‡mùÉ•èÉ)ó3Ñ[ºl½ùRˆ›Ÿ‹=ŽVlظ‹“Ïœ½Œš6©é/h'úxÚ “b~–¾n½‡óoa˜ð°5 a΃>#VA+lµc‡Ææ$A>ÆÄîã‚b‹¹DÄ®5Q,Îuè4Øp¤£=Ö=FªV³5=~ìK†ÁçëtÖuÁÿ$”PO¬>õ\¸Úßçí¬T¥9ݽû³ïضȺ˜HsÞ¢E zñâMœ8‘fÏžM«V­âÏUÑ¢EiòäÉJšê/êÛ·/íÛ·bÅŠÅÒ( 6d6·éÍ›74iÒ$Ú°aƒz_ÕŠŽï)[¶lÔ¹sg*Y²¤NF:uRdš{,¿²bÅ Ú´iž={vB™5kÖ4Òt°eËZ°`ú,ãúeΜ™:vìHeÊ”1²évy¾|ùrš1c†šLLP«f£aÆq›>|H¹sç¦ÚµkS:uŒü8p¤]mÛ¶U|/r¾×¯_“››3Ú§NªVD§‘#GRŠ)K]ø§OŸøúþýûéÆ|?oÞ¼4`À5ùG'#݆™3g’··7yyyÑ;w(cÆŒ4pà@*R¤ˆ‘VðŠ´ÎÕ;¯m”z !…€8ÜC I;å¨þ¯X°5X@vÍR·W0­Úôå ª`ÆçÏ—“²dNG¸ŠÂ/¿¼ò—Î}[Ï‹õņþÈAW‘iäðžT©B òZµ‰©%Ó—.]g¶dnÚ¶i@Ó¦ ñWvD½€AÔÀÁYŸ]ò"j[¥]‚€ ‚€#ÀA»bÙd*S®³s_¼xI™³•¡ªUʨþHJ’$!3š}.ßàa`Áø´sǦÆ#ª¸•¦¢Eò2CþºŠ3ƒØ/ƒ»PÎYX6dמCÄ]gèÖ¥¹>têÏ׆¾t²«U-£‚ÑG¥+*Ðë¤É 86ŽNóäÉsvŒ&NŸ¾ûΗ ¡ïwÀóè›Á I9ô «U-`‘5ª—WÚã¹yµ$ÞK¡¢µhènT°@.îçÍœ½”ãü è–›ßG@/ð\mÜ.W¶ˆrlÆ0Îm`H{öáÛ¶ú¥w<}Y¬.D?¸šÒÐÇç%³r`ÿuÛ~‹É<øô™K„I…1¢Ûª¦¿kîuÝÔDÁHž$xþü¥q¿‘ZYammÛd G}½d‰LÌÐçÖûâÅò1±lö‰ãP±’u9 drnÞºGU*—V’Œqèà¡“H;Û³dIO ÙDVCPO8ÁŸ>}ÊZäAyÑ¢E|ýåË—jâ⦊;•EMbœ§'N°cýèÑ£ Ù¯¿þJ%J”`ç7¾»àhGg8Å·oßÎNõråÊqZ8˜Q6ü§NbÇ1þ§7oÞÌ[ÿþýièС¾ 4h¹~úé'.këÖ­„mÚ´iÔ®];ίÛ™L$À¢E‹ÆéQ_J˜0¡š0KFÛ¶m£õë×+ÐÓ4nÜ8Nëh».]ºD×®]ã}zõÝÿŠöîÝKGŽ!hÄÃù.&‚€ 9øWähfè¶RõyÅB­Â A¼n\ÝK»w.¥éJíöôÁ¢Ñ’#ººÐqû † ©`r?zô”uU§LLçNo¡—ÏÏÒà9ÍŒ™KTGÐoÂ#ð8 0ÀoÓºž±>7Wš&‚€ øæ›°Ï„Ì„qý‰|È}x¯ÝÆ2"uÜ;0+ìtílG›Ö®™åOrvz »ªœÙ5k·#×4…©h‰:¼ÊNKÉ ó訳9¤^]·R„Õ}0ô›úôÃñq%ý™J”ªÇÎvĸá†6¦J[DIø2Ëùâþ)¥$µ!¶Níºí ŽYGlÞìQ¦ÐqoÔ¤×/{® 4gî £HBB%0C`Tí8GZ8¥1÷º•d/^£3Êùý¥Ö§W[£ˆ£GÏPÉÒõ)a’<ü~ºtÊñ‰\\i09’·€g¹‘Àê+MÝ*—´¸ŠÏ'V„ZÞ‹Ù¦M_L]»³»Ä¢­H‘ŸiÈ .“cÔêÇü…ªSêtE©ióžtðàINŠ †-çÿg:d܃õàŸ.\`ç50œú½{µ&,÷†ö/¿pÊO›±˜‹¯¦myòüd~T„:6£Gö¦ÃzЮ݇yéïÆM»UGÒW/=¨…æ~1År‚™W„Xÿï˜Ï-ŽÉo‰ÅuUؾýǘ)˜'OªT©’Q5ëtú†½ë¾u³ÿœˆšx`àYۯ߫´ÿ›‘ïó/ïßÐ~ÿ`ƒy£Ò«W¯ö«P=cÁ0‡œÎ±`àt·6°Ú¡»Ž˜0Ú±nN“&MJ:srõê3Jý¾®1$Ctèjìß‘Ùóúš³÷Ñ£G£­›<©cç!´e«¯†·~&ÞUǨßö*Àæ"µ n‚¾¢û!ƒ:³„Ëï­FÃü¹ èaÀôô‰Ô£×HZ¼d­?L!i2q|‡YÓˆ[£!ùS©b‰€oÜC Ù¶íõGÐÕœ9ýV ƒpТyžñ6ÕßjάYÓÓ¤ )›’GL—±„!y„â9i“Æ5ye£Î× ^U}¢ûý;¨Uù¨ÂÈÇÇÒ‰‹~8ÚŠ †„ }c#…èÃÃaa‰;6×20ø®„óŽ`8xa`†crã•Gqz°ÌwíÚ¥VÄgÉN¨þhéHÆX[ñâÅ)_>ß¾:îANÎc-3zôhë,|>kÖ,^õÓ¬Y3‚ä ÿ·íÛ·Wã³çüÙ{>yòä|J•*eȹ¼}û–<==•´VT–ÐÑ“¯®®®Ô§OfÇÃé©–à´Ëx¨ƒwïÞѲe˸κ‰Ãw% ¾÷ÀÂGû1™ Œ9r¥tëÖM“ðyܸq©|ùòܬL‹\èߊ{Jª¶ÐOÅ"W㥵aÇs½0 (æ|ÄáîŒõ«Š–"í C1’л¬–kW¬¬:ujIöã'Ïxé¶Î2gÞ Õ¡óíüà:ï½ûŽ&hÊ®´#xjÛöýœ|ЀN\^ÞŸ³3ÓËXK–©ÏzªÑ£E¥M[öpç›\\’(ÃEfã ³kJ—ípg€Ô,C-[¦oÐ1]¹j3/ù=qâ¼Nâоpá<¦ž3쯿ÿb‡;‚ aÙ«˜ ‚@ø@@;ÂGmýj Ýê%‹&ÐÔɃûôÝUrX9'¡K²ÄÊÉžJ1K£ùe°q„þÉ䉃ØYzçκ¨È?ü…Ò¤NA®®.6rK¬@f% ;´?`veZ¼òï?îØ,«þ6m˜G`w_½v‹Yìv™&M –bA¦þýÚœÊç/(ö©"@è –¨W`uÃd¾½g£l0­+6ÈþñÇŸJ7ß/èè©ãÌ®a¢€x/ˆÑL!‰“V9ã“©÷â¨ó8 #ovfçÞû§¾Ræ$wo2ŸÚ<^å5Íæu\l§ä¡åñâUº§ÞOµÚ }zW‹ª·®ïã~.VäúÇÉ_¡|ñ1×4O§úõªè[û§|YèƒxR@Iý\<·åƒ° àÉÓ”BõÝñMõ¿m™#øÙÊÞ¯¥H‘Âh¾/!¿‡»Ùñ‹÷}ö?“DpVÃ!=vh¥ƒ‰ ‰ìí™Yk]§3gÈר3-Ý’3gN‹$CŒ1Ââš>©Q£†>TãºËì°G;ÌבR-0°üaÁig àpAæ&C† )±’Xƒ1tfÜÓ¤Ic‘VO(`XäBà™’XƒauصËöÿÇ"*ÒÚ°€À­·ÃB5"|Äáî„Wlv¸¥Dš"Á^Â2lèLbI+6°ºvi¦˜ÿ¦açÑò˜U¦AAh¼ƒñ´vÝv¾#F4àêàW mÛì©–‚âåÃXâ CÇr53¦¬[ȉ#Á°þ[µtç º³‹–xÓ’¥ëŒÀY‘i¢ ‚€ ` €ßEÈnØ’Þ0r€>dªTÉy $i¨ÝqÁL^°~0Xû¶˜ûÖé¾ä<^¼8ÁÎLS«‰ lÉ0IƒÕ•öVXÂÙ’Dpl…×&#[ùrÅ,V’7BøÎu8ßÅì#`oŒ gv@ 0â¡õÇ0œá 4P+ßqpT[yá̶6°ÎaZÿÜú>ÎÁP‡™‹ò…þ¸¸øM*šË6¯‘ìþ *e§]TƒoA†Õ¶L_×ét{ïFß—}äA ‘š|†ÅˆƒÜjø­¶Ž<HKÃö¤ûwPÚô–ƒa­ž¥>âpwò›”\ǶÇ~jÓº¾Ò ¯ï¯n][6³5oV‡êÖ©¬áÜfözêÔÉ`]îuܘ…åµ›6ï1²ayjÃU9*œë`XY¿30¾víðÕjG`*8Û]]“YœÂ€: f–ñÀHpWÈÍ€µ¾{Ï^e°ný–Þ±Õ|ë´­4rMA@AÀù@~ä©b–ƒ„‚À«+¼6…|XøFLál‡~úرcÆè@¥ÆÓ˜æE1tÔa)S¦4_¶8†C:æöv @IDATÖìy|Æ ÉþùógÖx7;ôDW›fŠƒ¥¯õèõ=¬¼€$‚Nœvé²ìíÓ¥KÇ· ‰ÿæÍÙoU ØêЖ‡ét|"$P³ÇÏcãŽ\BfuZ²Ã=¼®$ ]´¾üi~¿h_^–”ðâ@üz…üõ0¡ÇùÝw¾Á»P,EÍ‘#3ëZ×éà0Gkg»9-‚9„€Z1bD7ß’c  -ÛeK&ѳÇ'iά”?¿å’RÙä’ ‚€ ‚ÀWBàÕ«7”ܵ ÅŒ“ÜëwbIT¥LéÂd^û•ª'ýB´ó;oÞ¼%A×ÝžM:•ôú>Ћ/æÓÒ¥KëËþöZŠPá°ÖæååE¸Y›dÉ’éËþöpæÃéŽ ¥¿1[‹-”ÔÖÔ«W/¾”vi'½uÀVsù8F`VhÃ#¦ÈС–«š¡O€¨ &TãÈ ­Æ€ž~ïÞ½yCX1A@ˆ‰€0Üü^rà:ùÑR¼ fÀRf¬@ÀM[HÎ,^²Ž>|fê(A@A@ð@Þ¼ÙÉs¾Ú ¹^È;·Z |Ú¶mKgΜá@ Û·o§ýû÷sÀÚ^±bÕ­[×hôÒáTÆ5°Êá0ýú5eÉ’…ªW¯n¤³>€TÍôéÓéÔ©S*¦ÃÏT³fMÖ|߸ÑwÕXé±,!ƒ ¨nnn¨“`Ã>|˜vîÜIñâÅ#…¥]p¤Ã yS±bEE¦ÊîϡΠԟiÓ¦qÀ׉'Xý T+£/¼ÇG?þø£NîÐiu Y´'Ož<å“D‚€ á q¸;á} ÃÝ  †`‘jÚ¶MJ’8A–*E9ŠtS‡éFƒ»Ò¾ýÇìJÍ8Zž¤A@A@@’˜8~½zý†¢*GbŽ™¨p¡”¸{÷®Í'ÁQnË>|ø`q9nܸÌf‡“ùéÓ§ÌOÀ—„tóæMfo›5Õ‘9Q¢Dì8‡£r(3f¤˜1cZ”kïÎuOOO‚F<âÿùÏT èTÒŸÈk¯]¸ÙÜ3õ†ÃŠ;æ?/¼pëÖ=Æ¥gï‘U>{·Z´êCƒ=&[\('áµ}?ã÷òæÍ»ˆò*¤‚€ N@`Í ojÛ¸oŸ?}v¤HA øˆÃ=øØÙÍ) w»ÐÈ A@AÀ&×®]£-[¶Ð¾}ûèÉ“'6Ó¤H‘‚/éì+:7¥³ç.q¹ÉSðù¡C'-ê™=ÇË»½qC#ýèÑ3ìUœÇ8ÌöêÕk&}ó¬ÂýÇýAûÏž½0g³ûØžñ@ÚØÝ¿ÿˆ&LœGù U¥ø‰rQñ’µÕwž—Ç·¹A{ê4çÇqûޏ…‹ÖX_"·ÁùÚ‚…^×òsÆôøðyô‘SÚ¢T¸Ø×Ï!ò(“¦Mq£_~‰­¶8”"Ebš9}¨š Ê‚Ëb„žf͚ūÊì©rÙ²eœß¯¿…öÔíŸ<˜8Ç8Ö¬ñþyöO½RVüŽž›žì­ááƒGtïÎ=›ÙÞhü–ÛÌàCâ‡èÖM¯±>dñ– ý°gU+ò>{úŒn\»ù]ýóÖ¸$„ZÄÃÝ·ÖL¸‹‡»ý߸q›ƒV… ŽråÌL³æ,§Ø±cRßÞmiý†´Óý UªXŠ—ýþðÃ×¹"|¡®ßàN—Õòéë7nQÔ¨Q¨LéB*_q£ñ'Ož–ã ´~½*¼ßà¸"wžP†ßÒ¨ü„v­ ’x¹Ü§´PAœdø-5=}ú‚JÏOyóf³Î.ç‚€ G–9ÌÕÕ•úõëÇÇõAK=<<ÔwlT–rùñÇ-ª4h"ز÷8‚’8*W®L:ð(2Ÿ:uŠ.^¼Èå !3xð`*Z´(kªëú ù2qâD¥/‹ëÀ1Hq<Üað~Ï‘#ë?‘#G&ëUªT¡µk×RÇŽ i0ì öAØ£®Ëú´GðÓcÇŽÑ‘#Gؓާ|ÖéwîÜá$HÙˆ >!ðîÝßT¼Tm%wt‘ð\“9s:%—ô‰õÕ·nÛKÖͤßËáâ•«¶àô1¢QÆŒiéúõ[´q“;mÙº‡6®ŸEÿ(ÝÒã'Î)BÚ+°§ç=züø=ñÒh(W¯ÙJ‘"E$—êå8ý€z*Qº.¯É!zîú™ÎŸ÷ ÇÓî=‡iûRÒϲ AØây)cÆ4ü<„´µëvЖsØcþÎJþÃnzÞ¥Þ}GQêÔ)X í>|J=Ë ™³–)"5‰óÿhÞüU¼mÝû‘ÇÅ+,^‚xT H>r=€bÅŽeÔgnkðØ=ztêØ¢ ]ºp™ÒgLG{O¹sÞóg.ÐÀžƒéÌɳôâ¹×$wŠTÉ©dÙÔc@7ŠÅëÙÞ¨Øt€çª!®ÃiÑìÅjeë?ôÓO?QúLé¨e‡æä\½¢)ç×C]ÐÓMýÆž¦·oÞRôÑ)WÞÔ¡G;Ê•/ç׌êèšôÚo­Y¶–ž<~j\sJ›Šš·mFõšÖ1Òä@_YKÁÃ!è—‡TÊ*½xé*MWº£;R/uèÌÙ‹4rÔ õ…W‰êÔë@‡œbo'x‚™­~ÃÎäR³5Íž»œ_4ñ2 ¯¯#§Ùðò4W½Ôué6Äë0]qöÐjßq -Q“½ˆ@ø›mÙòìi6gîJ~á{£¾€±´í¯^»ÕœUŽA@PðÀzâÄ Z¾|9ö´§ò}ûö±N9ˆjx}ùÇà-Ö¢E ª]»6T;wVÞ†¿P† ˜´†F9<éÍ=>ž=c‚ºU«VL°CWýõë×tôèQ‚—œ6¤C²¥OŸ>L0ét½‡F;”W¯^­“ Â=EŠFšù/Ý0=~HÏÀÐ6ˆýtéÒYíhêÔ©Ô²eKÎgý§ti¯ ~10ýb‚€O@Ÿd;ÈÅÛ7ÒÉcèÜé-4°Gv ОÆp@Sà[ºlíß³œÖ®òªó×_ã0.»Ý½_Îhõ§{ÏaL¶ÿQ¡y^ßOǬã~$OžXµ}“ÆOœkUÂ÷S¿ŽÏ¿¸Ï¸÷ïaOo¬pX¾Òk…Zókº‡õëUæÏ&0.^üº 7ðNL:¹š\ÌBŽú\ ø ´i]Ÿ.ÿµ“¶nš«»f쇸u¡•Ë&ó¸7®›E;65®É ‚@À!ðéÓgº~õoÏm¬à­¯CšöÊQ§­^¶ŽjWÄ÷¹Ozã:¼ÒW.^MÅ)Eñ;ÚÌm:v†ª–qa²]_Ç~Öä9T*oYÚ½c“í‘"EâËð$Ÿ6~Í^‚®y\3±8n\£͘8‹ÉöÈ‘#ñóÍ™g©YíÔ¿Ç ‹¼8Y—?‚€F@wDîÍîXm¨¯ /…3¦áq.Y4ž_æø\y®O×O}!駱5þ mÛ÷[`Ñ£[Kºx~ݼ¶ŸV¯œÊ/¡ÅŠæ£‘j9³¶œÊc~ý¯óY³—óËØ›—èï7¹Ìõë·©ZVNm&ͣ…rÓÎí iЀN4îh5ÓºII$0~Pt^Ù ‚€ `‰tΜ9Ù“»^½zßM>µ:sæÌ”_µj“ýÅ‹gsxœ†ç:ˆ?ÈÄŒ5ʨZì[·nebä?Èu#E RH³äÊ•KyÅf4ò?SÄ<Ì'òe’)tsÕØ±c³‡=<ím™öðǤ ;¼í!1Ó¾}{å-êÕæÝ»wÙ»W1± x[–?~î;¼ô=zd+‹Í4ÈÛ ÿ ùÅŸÈ’9=mß:ŸöïY¦VüjdÓ«=@´Ã"GŽÈ{<_âÙ«ö`+—O¦÷ï.Óæ³ùü[fÏ]ÁY5¨ndÕžbð>Þ¯VóÀÏž=#?¡n½šoÒ”L^wlߘ=ìuý]ÛS¼xq9Èå©St2§YÓš|1b*U¢ ÿüs ~–Ãw<åK÷J¿{÷k€áÑ#{1.ýú¶7êƒç:6ØÃ‡_½Êp½Kçf8ä了u¼<]1–{÷ìÿ¿Eù§OŸÒ7“'TØ{½ìã}úÔÁÔ±CJÿë½B™o™_ÇÓIµ‘0a<®öe/ÂÇpÑæ×:u¹$J#½x±ü|ºXMÀhƒ“ ¬^ʼwÔç•—,Q€'c°ú²1Ööë¯qU0l¯IU¬nÀgFLA ø!°}ÓJᔜvŸØAž/¯‘Çà ԶkkîèÝÛ÷¨WGËÉx=‚©ã¦«KS­5hòÜ Ôs`w&ç{wråwžDê·jý®Õtã¹]}|‘\‡ôæß÷›×=™ØÖõXï>A½õ Ï<èæ‹«´yßzJ’,1g›8r2;tÜ(‚º:·ìƤ|™ ¥éìÍtóù:uí*VÓ[5lküûwPœÓ%.ßoh5Þ«œžùÚ“ˆIôüÙs£ 9„pwðg@<Ü¿àÌjé,{6/r¦â%ùÜÉ)™Z"mù¢…%Ø=¥±j9qÏÞ#hØð©êEï~!µ¥? ^&ðR ¯°‰ãûÓéÓñ²bnHý‰¡¼¿° hðì‚Î*–÷ž9¹Y¼m4H²‡ P«V-ö‡÷·iä%ãScæÉBH¦hoŸò+}ñâÅ4mš—G(4ØwîÜÉ:©ÐVïÝ»7;wNÉ<üFXfnvŠzÑ>ú ïü´i¿Ê4X· i–©Û2ÔñôéSåQÓâ2êß°aƒ’˜8o‘ QiŽjժנ_¾|y¼ô£D‰Â}„ü ‚¦¢ŸØl~#ú÷ïÏÝS¦L±•Å[Úû÷ïq¹ŸjÔ¨ÁúÞ2H‚ ðȪäÍ“.^ºFðªv®ò'¥ý­8›A‚7z·®Í9Émð$ú5AN‚Nyó–½è€ÒI×½¹Œõñ9¥í~òäyJ™2©’‘Èe\nݲæðÜ.\¬ÅŠ›•J*‰™±ãæ(ç¿ùÿ ™!3[³n;U¨ØÄØ*UþSý|äk×Ô³’¶dÉéCÞk –¬Y~ã—f}1R¤|hvV)Q¼Å›¦L]DMšugý÷DIóñ³ž.gÞcLf©AÊÚð=⃠$¾yi ªGïIz"iö˜_ǃg]³!(ìóçÿŒd¿ÖiT TåS¬x€aÙýÆM»,&+õ¹@{Xa!æ?0™ÉdL°c"“ÙøMÃj2³AJ±N0'NÞOŸ>W£™óécLfC– rh‰%b)6ëßx×z鵊+ò¤<ž°Â udÏž°â ¿çfä=òc2Òœœ¨páÂFüŽV¯^]M¾¤áÉwŒ×7­ö¿þú‹ÛÄï¼Om¢rx©öìÙSM&æåþÁ™`éÒ¥F»ú+ãð;®1Æ :Hõ™ãÊè¼²Â*xþX¾i1eÈìõÛâ¹[Or©ëõ¾gç^:qä¤7x^½|E£§Ž q3FSµÚU¨t¹’,ë‚gyüž¯Þ¾‚òÌÃ’¾1ŽI­;·¤Ž=Ûq=‡ö¡­¶y« þ¬Gíºµ¡hê¹ õäTÒ0+¶,5žFúª”0¨×`v$H锂f.QüQ¢\g⤉hWMãÙdì° œ~ãš'ïñ'’¡ƒÓ¸>Èá /5iн_å™ÿÒÈ'‚€îø ˜_P}˜ªROXè—žÇW³ž__ž ó‚€V…1j:ýõ×~ùܺmã¤_Í iPs4ßa:Øއ éÆžM¿—kÀH,jŒß¨VvjùôdAÀ!@cÙsçÎ%=­ZÕ‹âÌêú…fxÞÃÃÞÝÝà]_¤H Ò§O¯³³„ úÓ§OÓÈ‘#ùE{Ò¤I¬%tŒOÿÖ…LíÚµ£|ùòÑøñã}ô„7e瀮¨YÅü‡À»wïø³ZŸ«Îž½¤‚äçŠÕk¶<Áë*]íμKh qëJö® Ö­ê±ÖûÕ«ž,ÍQ¢TjÝÖõ›@kïö†õ«Z|ÞAŒC¾düXW*W¶˜š$ú‘Üwä £©Ó3´Ï_«ç-m¸æ-Oî¬Ê »¨ LMg±hÃHTÚKÝœf>F½Íš÷ ¬9ÊQß~£Õwß]ʨ´å8zî¶Ì·ÿ_[ù}K{©–’àeæˆñ|Oæ±8W*Í÷ õãÙÒ½Há<”8±áà¨Ïú•¢bß&‘!w†ß^übò[¯Ú*S¦ŒEňw‚çHÔýüóÏ*ÖÃ?¼ª ¿…Ö†µ‰àò¹sç¦!C†ðs§#`{Íš5 ÒyÚ #ò+õ°Z ub2ðмÇÿ ˜ ¨KJW=~ÂøÞ €¼¶cÊëÜÚÒªzuÕ²HÖÞç¥ùž½cÈ—Çô0d×ê6d]¸pE}y®Uô¼´{1"èv‚·efm/_z=àERZ\Ú<ìæµ}¬_zçîEü¼$å6Ájª\­¿ ê¼²A  €w‚4Ã4émí± ”R¥JÑÙ³g©B… L´¿zõŠæÌ™Ã/½( )–ĉ½–Qâܯ¦‰óT©RÙ,ªõÒñ’l6sŸ}k/³ –,Y“ðƒgHöyóæQ·nÝø%d6ðx‘†ÁÛ ÁTµw?<×ñâŽÍ–HÀæWƒ‡ ôæ1Bxúf?‚ÇÆ‹ç% á[Þ°| ÏA~@îd #lׯ_çtžÕAÖ«B nˆ óLi¦B«|İžÆ°¦M_lãË®±Ê.Uª¤ŠïÇ×ð,3^¿ì7`,MVr/}{·±)ÏÌÐÿFàx|–8Þl<æÉ¹¦Mj(2¿>IÇŽ¥†Mº0Ù½rH«8¥JFÇŸ¥F ªôÞ͆{ùßÿ§^X"­;o¾î—cLB@ãZñгO”èë‹|»ýýRÕwåÅ8a^Š@¢˜Ѷ~ÃNZ±r3eQ++;Ù©+îˆñø·NŒ S§-¢%ËÖN$õêVÖCU¿#û¹0*VøŠ}?Æ ãÿSÄ#FŒàŠ^¼xÁžàX‘våʥşš‰ä•+WòJ+HÄa¥ ÞñÖÄ<ÒAdc…V‰%˜ÜÉ B×Ð>ˆþ ðêü¦£½ãÇsÌòfC<˜¶mÛrûø®Ço<<Ðñ Â^î0x¼ƒìyïyc×ðbO›¨÷Â… <é€Itw•3 ¤”°SôÄìCß§ Íñ{§ ¿§Ø`ú7J¯>ƒ,Š&Ûq½dÉ’Æê9œÃP¿¯øÿG¬<‡À°Gü¿‚žk©+rÇ¡i¦<×gÏYÁõ5jèE²šq¬U£¢"”޳^:‚´æÈž‰îܽO0ÓÄ{Ú•8`ë‰ç(gžŠTÙ¹´òÎIk•¦;È}xéÛ €inËžãlY½t`!XþÆü wïþCš7•Q|úÌ%êû!¡qnÏÆåÕ«7\oÖ¬éi`ÿNÞŠâÿ{ôÈÞT©r3êÔÅŽ«ñ‚€‡g;œ- ‡Ò¤±‹·r>%8b<Q'V…" )p†áþFêE2â<0>ø¼ 1 ÍQŠäI ì÷H\8Œý:V·AŽ “ì˜Ço´–cÑpàÌVðn<‹àC›Î‹IД)SêdÞÃã[{»[\ðá$nܸ”-[6‹«9rä <Ÿ@š«˜ô 9d²–Ó«Ø gmHC¬G_·§Màƒ‰3L*X·©ªë{˜ F,ó¢÷ß±²°qãÆ¤³ëöe/„TÌ\Õ§OÞŸ³Þ¼~ãëÐn¨ßE[vïÎ=ã¹-iò¤Þ²X?KÇK¢ªçš·Šºv康üH@l>Mf;¥u²™ÇSÅ’·¹µyÞðä$ö 'àcýlY±ÚhÕºŒOç©Ò¤RZí]yóé±C'hˆ‰ä¾m7÷oò˜©4pdŸŠKzC@÷0vÃópA|wì<ˆ»X¯A'Ú°n&%Q_ˆøB.S®=~pBy?¤àeÕU]ZÒÕË»©WVJS÷9/‹E°ÔŸ~ú‰_Æq¥‚EªS5—VÝSl •/˜7švï9BS¦-T/¯wUžd4jD/j×¶¡<Ñ£GU^ÿÐaS˜ÐÇÅ8qb1±ïÚ§­E^9A@#€:xeEЉ=µ±Ì˽ᵅÀ\X>à£Ú@T‚dÇ éöíÛ™pÖ× e‚—;3áŽåÙð°2ë”ëüØ#8Ì\†¾ãÈlçxÉEá¹þΘ1ƒƒ–6kÖŒ½¾P5^ø¡[ÒTl0x—bBÁšpç‹ê¼Â¿¥ù®óåÞú!(û∶5™Ž{lM¢;ŠL7/?ðpD =l˜€‚4V` mb÷öûJ¢à Uϰ¬ŠpÆóOã¦Ý9Hü„IóXþcËÆ9Ôª+ë«k’xU¯¦$”ä‹¶Žíœuöœå”3GFþǃ?*”ÐÙŒýŸÍj)ì- 1•IeË0̃t¢ßËásTîÛR»Xžf›—̺öwí@½{µæ|þý㤖§O›âF»aÏëÍ[vó³]Ç•g[8B»‹—¬#³ü‰=mbBÚøãÆÏåz=oݵI¸£.à´eÓ\ú³EO–(Ôõ,˜“¦NrSZØ–«zôu[{GŒ' ê„ž箃¹ÛÖx·Ï…-lÃbVQãù¿ÁxÆ@|¬†A ïoÈtiƒ^;L{këtìáÕm6ßòb5´Î5m.gëÏ ø¾°6x–ƒp·ÖƒÉo6íñY›NÓyôu{Ú4OHX¯F¹8;ÀPÈvÈÛa&'à€ ÎÐx Ï,Ù >!ðK¼_ŒKwoß3ŽõÁ™“gõ¡Íý¶;èñ£'ô˯q-®/ï5ÑÄŒ*Xº=–)kB@ÔÍk·°.z«¸-‹f/æI3Ô•9ÛW)asÝ g/¢#ú™“˜¨ß¸ÆëÙ&]¯`§È€ãs§ÏÓöM;8à©õ÷–k·äñ—%M‘”†Lýº¤ý»PxåÕ¿tÃB–ŽÁsXþÂy•·{vJ— ½Vq`®^¶”ڴ茜„9„pwÀ-7ÿˆ[ÿã: ¹PS%d[°YÛÇ÷W$xZio+$´&O ÕŒfêäÆR£¿ß\2Ê™"EŠH#‡÷äÍœn}¼}Ë|åÑ5Ioß¾ÏˋӦMi,×¶Î/ç‚€ ËÐø„ ‰~9D:›­]»–uUÍ„;ˆxè §­åY5jDX: oumZÁüb­¯a¯=µ‚p‡—9&2ûôéCƒ 2êF1hÇšƒŒa¹6^háÉ{¼°¢<6¼P£± CÀ™®Iu½hÏtóhÍd:ˆt3©®ÉuŸ4ÿwïÞm®*ÔcûRõÿ}—5³!‘÷ë——Ö«—w©I-&¾1`8ìØ¶€IxäžÈ#Ft <æò_îjµÈ?ì]öæÍ;Z¹<ÅW/Õ>ývéÜL94`'„§OŸ«øq¹]ëgØhÊûlöÌá4uò åaz]}×ýO9-$·Ð9‡F¼µN<:Ø¢yÞ,:«N …n­‡Þ¤q ždÀŠDèC»^/Û†ç5Ɔ•†ðT³ÕÚøï£×ÄŸ¹½áC{а!ÝÙq#Z4/onµ•:ž×ð}ž{ò䉌{c®Óžc{ÇsóÚ~›Õ¹ömGØÌfoÀÈÖøP—¾¿‰•sK±¢ùÌÕóq@~.P¡õøàeïSß¼uFx¶ƒlGT¬ Ãw§6ø6<Õ!£ƒƒ›¯¡¬Ù´W;ž5°ÒLKÊ ~Ûµ„œ¹ŒOÇzÃïùûŽZêÏfÓÏ4: ÏN ´1Ö:uêèdÞ# †/Ç]ÛwÓÒy^«Ý|â>4¨Ö˜æ®˜eî¨gÌq\ÁOXÕëÑ¿U(êÌúèµ*Ö¥«çÒ¯_& ¿îÖ{(Wƒ€«Î.Þ9#\œ9i¥IŸ†j7ôZ öF.ÍH{ꛃ¹vîÝêUiD—.\¦žú(¢Þ•Ÿ­ðý5oúš3òLdû³‘,Y"5Aõ•8C.ÛZJÏ\ Ï&æçó5ó14¹‰"1!oÖB7ç1ã9*Mš¼™Óm#/$ýiQ¢DV«g2zkŽþ1|cÅŠiwñãÿBجm£’óÙ¸i—u²Å9V¸ ìÌiŽÏ÷Ö "«¦Íð Î Ù!ýûd1uÜ>Öý kçðº†%L˜Ðâwøüùó¡­1ùý÷ßY3“ú-Z´PF¿ò%ç¥3w'''Ž‹sL²kƒ„ ~cì5ôcÇŽåØ2º êÀ3 ž]ð ã›AoýÆ•q˜¼‡´‡ö:̬Is{ÚDûzŒ‹Ás6ëXQˆI ôº÷pœ@0WfwT/U~×®yRï¾£¸$–#OWA¿ð2‹ ¨ðž–)ïÙ³‡±<º£ö>äôè]]]Ù#žx1„§: ¼ÐˆÇï„&5Ñuµ6x¬ãeÞaúEÁÁ4hÀÁÏàimQ”‡ÇÕ‚ xh½âeÛzù¸u>ë]xËCê#Nœ8>e•t!Ét[êúóì ¤ZA X!LÃ#~ääaÞˆúYK§3Ù>¬ÿHzùâ%=åµúä}ƒæõ©s{,åá¾bËRrë3”Ö­XÏïf§Žæ60¹P¶bê;¤7%QÏb‚€F@w„ƒößz¨pP³R­Ÿ?ÿ§¼t~$—êå­®øïtì¸Ùtä¨×í·jŠ=*MŸ:ä[Ùäº |AÄ4X̘1ÙsÜ^¢Ïsð²Ÿ8q"{˜£fxöÇ9È-³!> HqHóùÖf… D>dý0.mŒŠ¶´“®Ã‰zíË—/ç y±òzX= &„ðÿÙ”öÝÚ‚ B¶-{”Û“^+q}oõ:UÙsüüé Jzò9¥WÚèðô¶6ðߪ eÊU*ËÛ³§ÏXî^íðžÇw€-C€RsÒG¶ò¤ÁÍëž¼Ê/S¶Œ>–E}MZ5â ^¯\¾J±ãĦ”©Sªø‘½52}Ú‚IJÓÝnݸ­œž(g$äOÁÞ÷Þ HB˜G@w|ÄÃÝ ú³J,“ž?w´?kñ^<]ºTê¡Ï¾#hÇ‹ ‚À·X¸p!õèуÉvž ˜À„7—™p‡÷7–ù‚¨Ä‹X‡¸HmxXáå/šæ2ºNGí1QC_Ìd;tQµn¨™}ú”qƒG¼üfšQ’ãЄˆí,92×°‚ä{ËúÔ ˆo¿zÈëº ‹ƒÍ/ÏyŸ¼ç­ë‰ùsLŠ™Ý~y:ëòrv°) ;xøHåG9À! V"¨61A 8"Ïox9üø‘2dÈ`¡÷Øý}÷îíß¿Ÿ^½zÅ/7yóÚ^Š,, †—–Bã¥Ä/^¼ ›Ù†ÊKŒ±„/xÚðr‡Í*Uª0A„;^RoܸÁôZF·‹=¤W`ð®G`-<„BæÇxiƒÁ cœ?>'ƒ7HrxÄcÓ†~cì†eë ’iÓ¦5x]& öxá>sæ •.]š=åÐ_xÌ+V, š±õX“é¶<ÔƒÊ3]Èôû±’Ž ‚€ `X¹†UgÖ†g[Òàö~ëõ 7{ËXçƒç½_Ú´.s‹ى'¼•…^%¼¦±Äø{ ¤><ÒÝÝÝY'ÞHæ%Xº Â2+ )µÁ«žç £µÁKÚ¡ð¨‚w7¤K´8‡4 ¼¥Ì†%Á¯.ó ‰±Ák ûÈ‘#ÓñãÇ Ú£xó¯!0eÉ’%¹hc±cǶ¨ºªš 2ðÀØñ¥1=wàOt=~,S¾xñ"/‡×&k¡c )î©# K¦5VfÜp ÍÕÓ§í“ÌrDß‚ªN!Óƒ yiWA@A@ЀàBÕâáJA@ðx/^œ½Ú!ir¤.][ÂË¿E¸ƒðqŽ¥µ ¡åéááA ÎQ^ä]´.(²ážUðèvqqá%ÊðF_ºt)Íœ9“u4uYxøðúõëgA¶ã:¼ÕAÞ?yòDgç=–¸ð>zô(“êÀ¿\¹rL®ëeãŒÒMÈô~¥ÿ‚€ !Ì™3sàÑÚé· ؇â:Ô¬ïb_fÉ%„q„pwÀÀìáî€ê¥JA@!Àó{Ñ¢EtíÚ5ö47“ÑŽcÁ\ †`šðj†N%ˆh³!à(dM a2zôh ih /^¼ØÈolÈ‹º[·nMÜ¥ƒj"£-¯&H« ˆ(p8pàÁ“XëžCŽÅlÚ ž×:à—ù:Ž! rxŠ£hœ/_¾œ oK“ Ðyoذ!,Xк:?Ÿ'Ožœuä!µ²¤{H°Q£F…„núØÇo‘éø\!°¬£ /X ½Ðõ^ëêc/žéŽB_êA@A@A d! „»ƒï—x¸;`©^ÆÀã»E‹ôùógö:w$á’zÅŠìQH s¹·oß2B Ë!Éy™1cF Ïv*ag䢱eÚÃ4'N´•Å"ÍÕÕ• D𠆶9ôËA”Ïž=›‰xLPêïIh´0€&OžÌy51¯+„÷>$b 9Ó²eKN†T ´à{õêÅžô Á÷¶mÛhΜ9„`¤ÐPoÞ¼¹®Æß{­ØÄ|>|HÐðǦå]Ì{¬°pÔd¶és£–7oÞ)Y©gÑ”´†øðá_:uú¥vJ¡btÄ ÃHÈЉÀË—¯Y½Ô-Á?|¤+—¯£IWÂ*o^{qauü=n!Ü€¸£HtUª"kèŠgÍš•½ÌýÓ¼wáÝ'NoA:Qï¼yó¨ÿþ@çð9­u¹ÍÞß  !wÍp3…—:<Áá½ý-C½ðô…&<Êûf¨z½zõhÒ¤I5jT#;œ9r„@¤&H€Ó³eËÆ}†g}Íš5y<è?tã¡÷>nÜ8öÒ‡Š6hÆ#³fͨˆÒH‡~;ü÷ïß3Ñ/|Hß`2A,ð®dºÙC]<Óÿsñ½-öè5œ°‰ ‚€ ‚€ ÜxõebÍó†'åÏh[†2¸Aú:¸}ëNèX0•ïlI0ëlHìŽâ}ÄA Œ#¯q³| <°¯_¿Îúã¿þú«q ô &ðuݶm[Öߌ “‘™”dŠŒÖOòÞ½{³Ô ‚tBöAN}2H¬ÀàyÂÞÄöî(ƒñ@¦æ[)‚·šÉvHílܸ‘¯Á3_îH€W:<Ž@R1f«R¥ 3Æ›w9’BZþð”Ö“ðÂ×25æzä8`0“éÚÝÚC=(<Ó…L˜ûœjApf¬dAì1A@B XÉW"¼®@IDAT±b¥Ð2‡ X!.|8«9‚?~Ãq.xô2ä÷BwÜC¥ììéÓçôÃ)‚]¿¤C‚@X@Ä8„~üø‘½­çÏŸÏH1vH´€,‡Ô zïË–-c¯nÂoò!C†PôèÑiïÞ½L,;vŒIø¸qãr9ü®9Ì^B\çÅÞù`¨ÿÌ™3^'¾ü…'< $ùøñã¹ ÒqüôéS¾IàLàymîܹ,Yxõc¡X±bäääÄelýA@UxÒ¯½~ýš@þlæãÍ›7óÊ‚ï­;$– îd:HuÄ ÐrD!cé³ý4hЀ°‰ ŽFàêÕ«dÜ=}útjÚ´©£›•úA@B!Q£FáQ¥NçDÏí …#”!…4×hFëWm¤ø -c»…´q„”þ áî€;e–” j2 Y²DŠ”ˆG÷î=tÀH¥ÊÐŒ¼nòåÍš‡hcA ^š‡fùH¡¼¶áï›`á}üøqoÙvíÚÅžùð®‡^{ß¾}9(+äq@Úš%hbÇŽÍNᯠzï»wïfÉŒžê#´ÖÑçM›6鬶_»v-¯ À=ùí·ßzðÀKoØ É&dzH¾{ÒwA@A@A@Ѐ€î¸‹ÁÉÃÃQ7nlŒTª{™ sqq± Û‘RºW¯^}rÁXi’>UªT|ný'S¦L,-bTͦËÂó=z4ËÃ4oÞ\'$?@´·iÓÆð°G@S1Ç dºcp•ZA@A@A@ÀF@w nöpw@õR¥ „0 ·5jT€j«ë Ãa\ѦIóÒ¢E‹t2ï×­[Dz(ÐwGQmÐvy®‰{hÂÃ0ñ— A‚n¼-Â^ôÂzêÔ)–¢‰/eÍš• ã/æ‚#™ní¡.žéþ¿ÏRƒ ‚€ ‚€ ‚€ „pwðç@<Ü °T/„@ Csýüùó2/Ÿ>}¢Áƒ3¹]­Z5c4j-^¼˜ƒ«Ö¯_Ÿ"Dˆ@îîî4cÆ ‰rfƒ|K®\¹XžFK½ Ý$I’Xóæ2úÚåØ*æ74™®%]îÞ½KúXï[æEÈt¿ÝCÉ-‚€ ‚€ ‚€ $B¸$š_êw€*U !ß6Z¼xqêÖ­eÉ’…@ÄN:•:HÔB… ÑСC©gÏžtýúuúüù3§AÎÅ–ÁË]뱃˜Ç&öm„Lÿ6F’CA@A@A@ƒ€îÀÕL¸‹‡»–*†ÀäÉ“-zŒà¨Z£Ýâ‚Õ wOOON}óæ aƒ×sX6ŸÈt³‡º#=ÓÇO*«½Ð%Jdë4ñLËŸP» „<<<¿'Ö¦'¹u:V=!öŠÙ¬ùÅ0Y‹²b‚€ ‚€ @tåÒUŽK,z»õ ?Û6XAà B¸;à£ðÿç€JýQåÞ½G鯋WüQƒ ‹D‰™ªT.CQ£J Î°xÿõ˜ƒ+™nöPÏt}·d/‚€ `?üðÕ­[—lOv#Ê ¬˜ ‚€ ܸ÷>Åý5.ýøãvu Ê (?a|?ý¶=|ðˆ>úD '¤Gm¥ý-`㛼~õš=çcÄŒa´ß;þ¿ÿþ›^½xEñÄcÍúïíÚþì9½bé™_„wù^ ƒq9!Ü|s‚ZÃýáÃ'T¬d-‡É48>©>ˆ¸éy‡úõmĽæ…€™L×ÞèÖêA!ó"dº£î¸Ô+‚€  €4Œ_ w‘“ÑèÉ^A ¨7l-™·ŒbÆŠIëÜWQ§]iÛÆôòÅKB`ïô™ÒQËÍɹzE›]<{ò èéF§ŽŸ¦·oÞRôÑ)WÞÔ¡G;–@Ñ…Ü{@Î%«ñéà±)zôèÔ±Eºtá² Œš„ Ù‰òÚÚ4î@‘#G¢fmšP£ t²Ÿöx?1p­_µ‰%YPD~©r%©›kgÒúuH[×oÃ!-X=‡œÒZÊØ ˆ«K¹Z|½nãÚÔªS >ÆŸïÿ€‘ý(NœØJ«~ ?|’ÐO8(6jÙ:÷Ƹ#sýuœÐ5kt[ñ(°s§ÎSžôøxÍŽ<¹ñæõ‚äÌšekéÉã§| œÒ¦¢æm›Q½¦uŒ49Ùáî€ûœ4ÜŸ?Éd;¾xÿ¨P£•*C#XqéÒ5zòÄ+PghchSp'ÓAªÇï7oŠÐ~Ïd|‚€ ‡€““åË—:dW£È‹2b‚€ ‚@P"ðôÉSº~õE‹jW¬O{Ý÷qw@vÿý÷?tæÄYjV»;}ž\‡ô¶èêâ¹K©K«î„øZ°¨Ñ¢<ÉwnÝEûv ±ÓGQµÚUøÚ§OŸ¹œœ:v†&ŽœDïÞýÍ×þûï?ã'¨? èaÏŸ½à½_ÿ<{úŒ@XŸ8r’‹ÂS¬—ÿòàíøáã4wÅ,Š;å-›¬¶vÅêÒ§#ë?›Ön1ú—¿p>Lß;þÝÛ÷ÐÒùËy‚!R¤HL¸‹ #&)õW4jòpnãέ;F»Hxÿþ½qþñã'uþ¦òE*ÑÅó—8?tïÃ…GïÞ¾£«—¯Q§–]éÎí»Ôk`w¾.B6B¸;àþ™ ÷ öp×Ë=*-_:QŸÊ^ð~ÆÒ€ã}Í#ƒ[dºµ‡º£=ÓA–#à¨9è¨öL2=è>Ò² ‚€ýÀcÝ^Â]¼ÛíÇUr ‚€ 8xJïÛµŸzêÁåQ”ìÉ£§¨y½Vìa=qädú½BiÃkýæuOêܲ“ÅeTú°ñn” Qºsë.µoÖ‰ëjÕ°-eÍ‘™R¥Ie1€©ã¦3Ù^«A *P$?EЉräÎN[Öm¥®mzpÞ‹§RE„ƒÄÿØs0“í˜H5e8•w.ËΣk–­ãI‚ÃûRÏ}iêü‰T¬tQ&Þ!ɲaõFo„ûæu[¸ )S§¤,j<0ÿŒæ¤ÙTµVer=€':vlÞIm›td²}þŒ…Ôwp/‚ôÍêí˾Ÿ¨‚"Õ=oÜⶬžËíÇý%íÜâníý†ö¡æí›±œV `5Æ¿…JÇÄ‚XÈF@÷}ÿ¤÷‚€ Ê2=”ÝPŽ ‚@°EÀÅÅ…ÚµkGÿüó¯}„7òŠ ‚€ Á Ö£vÝÚ]Ê©¤aVlYJy+HðB9h -ß¼˜¯ê5˜Éö”N)hæ’©!BNOœ4‘òŸI¹Óåg‰“±J²fâìqF8€÷è©#ò,f‹ñóW}õŸ•ÄM¼ø¿š/Û} ÂÞç0Úf9—ºÕèñÃÇ,ƒHË$O™Œœ]*Ò¬ÉsXâ2.z’àñ£'tìÐq®«jÍʼÇÿŒ?Eªä4~æC¿lÅßYöfÕ’Õ\?¼ÓsäÉN±•ì ,œ’ÛAiÂŒÉkžœŽ?é2¦¥páÂñyúŒéø=xŒÏ_(¥ !ÜŠýGwܾàèáî€aJ•‚€ àG|#Óµ‡z`y¦kotku‘yñãM•ì‚€ !èÑ:;;ÓâÅ^d„OAäA@‚õ›ÖõÖÃÐ<ߺaS2,ÚöºïçÃrÊs\“íú¼ÊK—/E g/¦µË×{#Ü¡£^§‘—&º.ûý»°7;“W¬VÁ[Õð.‡î<&6¬ÚHm»¶¦êµ«2áŽÌÐ|ïØ³—ø5'‡rÚü3~xÔ[£-\¼ iÂýõë׺_÷ÐÊ×V¿JcªÕ°+U„²«Õy æáM_—}ÈG@wßÃà")ãàaJõ‚€¿8rä4-X´†&Mà¯z‚²°éA‰¾´-‚€ |Šùá.r2߇­”A@p,)œ’Ûl ½òžñ mpæüüù3{©#3´Ï§›á­Ü§OŸ8íǬ5nÎ=W6ÖS7§䱇Š!¡žú—ß|­úùs/øl¹²¼õ¡gYM¸oVúí0xœ'K‘”>xä¯ñlj‡ë1ÿ‰¬‚¦úÕ²çÎF]ûvæÀ°ÐwŸ=e.o¨'u:'ªP¹ýÙ¶)ýëg¿V-ùƒ!B¸;à¦èÙ4T-U ¡ ãÇÏ’kÿ±´uÛ^Žò\ ÷àH¦[{¨‹gz¨ú×Á‚€ Å‹'ü¦b¥™-Ã5äA@‚Z’ĺ_"D4’þQTAdkƒ„L‚Dñõ©Íý‡÷,Ò¡õîHÓÒnOöÜY}mê—_â׫)/÷¡ý†Ó…³Ñk7 :éþ «VË+ø+Žßÿó;¶ï¿.{xu®þ­Tr4èë¹Sç “W.]¥Qnc•´Î2Úvp#ÅOèû= ˆ¾HŽE@wÇâëÐY@w]ª†ÀéÓ1Ѿq“»Ñ¢|¿|ùšbÆ Ü%ÛšL×’.wïÞå—nó>°e^„L7>r ‚€ 8,_¯[·. <Øf;¸†]¶H÷òL¬¼ÑïÓÝ{ÕþÚ«í®:Võ€¼øð —Y³f a Hì9Èrë £æs!Óq©KA@p NNN”/_>:tèEHÃ51A@A 8"0sÒJ“> ÕnX“»÷êå+jäҌ޼~ÃçÐ×Ö¹wªW¥]ºp™zv裈zWöúþ÷ßiÞô4yôTÎÚwp/]$Ðö%Ë– LY3ò„@ïN®”4YÊœ=·÷ö]j^¯K®Äü9&eÌšÁ¢_ðráŽÉ„ËjÂVU¥Y[`Ž?jÔ(ܼçuOz¡4çµ&{ìØ±¸Ÿ¸8¸ï0åÅÞÏðæ‡´Œ¾oÙ”f¾XÈG@wÜCñpw¨ReˆDàòåëÔà8Z¾b“)Ü·€,‡ÙC¦?zôÔ®:}kϧkB¦û„Œ¤ ‚€ ¡x²[îâÝ:﵌JЂ@’äI¨}³N4 ‡ë²_<ÉÐkÏ?U©él õ÷?ʰç8<ÁgNšM‹ç,!§´NLdk õü…óQÓ62ué¶¡ãܨz¹Z,©R"OöV‡g:‚¢ÂðŽ>qöXƒ Ö}«Xµõh×›0q€-Š"»ËT(¥/ûÀÿo™ÒÓÙSçèé“g”ú×ß(B„tèÂ>ªÝ¨&-œ½˜õæçN›¯îÁRÊå7Ðõ5]¿rû íöõªý–ƒ‹€î¸wjŒaÿûŸq(‚@˜AàêÕ›4`ÐZ¼dŸHñ~Æ*m÷1dº–t1{¤ë4ñL3U¨ ‚€ À¸¸¸P»víH“‘"E"¤‰ ‚€ Á…kæRÿîƒhë†môüÙsîæO?ýDÎ.iääaLR›û>két&Û‡õI/_¼dR×£D‰L š×§Î½;z#´Íåy ™˜½§ÜyáàÞCäyã–Ñ\¶\Y©[O*P$¿‘¦bÄŒÁ;&`åË~ÃmY`Q¡£äÀ1žAPTX,åá¾bËRrë3”Ö­XÏ§Žæk–)[± õÒ›¥r8Qþ„h„pwðíI,Õ+nܸMÝ&Ђ…kŒ™u¿tðÁƒÇ~Éî-/fÀãÇÿ…'VR/ ã±ÌÌ‘£§©téÒÔ¿•ž˜âÅ‹'Áϼ!' ‚€ ‚@ØF zôèäììL‹/f *W®LHA@‚+¿Æû…¬žC>VR1—(jÔ¨”)[Féî“5iÕˆ°Ý»s®\¾ÊZç)S§dÒÝ\FŸ|¼oNòv\Ù¥a (ƒ&ûêíËyòûòzûö-%O™Œ%Iäk Òíµ€?<ë±Ù2ôwûjúôé={úœ¢F‹jà‹1N[0Iéä»Ñ-ÅŸ<~ü„âĉM)S§ è1ä¹Ãž!5MwÜ9‘”q¨Re°FÀÓó. <‘æÍ_E:Pi@wØšLO¬¢­ƒT÷"ס¥Év,GÓyî)S¦¤Ü¹sëdÙ ‚€ ‚€ à † „»ÈÉxƒGA@‚) Þ±ùÅ&NHØaîÛvÛ]m*Eö'UÒ8fƒ‡zÖœYÌI~ìÈñëÎBǧû=ú˜Ùcꬲ…áîà›*îXªR°ÔmÈ$š=gÏÞú·3#F  å‹+Ý‹L‰ž8‘:¶A¦û·-)/‚€ ‚€bÅŠñj8¤áXLA@üŽ@·6=ì.Ô¶kkª×¤ŽÝù%£ RÂÝwJ<ܪTì@@’ÅKÖ“‡Ç îE¸û¿‹ðb_¶d¢ÿ+’A@A@ð#X%W·n]‚ÃŒyÅœ«‘ì‚€ ‚@˜FàÄ•#azü2xAáîàÏx¸;`©>È€.\·®Íy{ÿþK·ìÞs˜vï9BGž¡?ú¹oïÞýM/U„î˜1E»ÌÏàIA@A@ð7"%ão¥A@"PÙÅ™ÒgLÏ-DŒÑ-IÕ‚€ à„p÷z>”w€‘äP‹¤`ŠÎÃ[WRANÞÓÁC'˜|ß³÷?~ÎnÉ™»wáj?)20A@A x#àää¼;(½A@Ó@ÛÜÑúæa`¼ @á@@š«1îaÑÃ}ÍÚmÊ»Ùg}‘ðáÑSªd”6mJúñÇÍÐúñN÷ôüù+n·`œt3Ð; Œ¤fÚK/À†ÏõAÀ{yÀŸð˜ªW+G… æ uã“ ‚@ØCàÊÕ›4~Â\B$1A@Ýyò̸„`¡… å¦iSÜÈzr™ ©Ó¡Ó@Z¿a'a2Ál1Ô2Ÿ† ª‘ÛÀÎ)RDó%9A@A@A@A@ãœ>~†úu$„{0ûüÌúêºó¿ÿ…º!è€nÞ¼cQ_œ8?[œë“­ÛöR•j- ²]§c?wÞJÊ’½œ ¸y˜Ìd;®ý÷ßtôèÊ•·3I>Úœ¹+©N½d;2#˜ÑšµÛXþA^­íÅ‹WT¶|CêÝw”ÙŽ|ïÞýM›·ìæöOœ8gQÔÓó.åÉïLË–oôF¶#ã«Wohì¸ÙT¡b ¨dœœ‚€ ‚€ ‚€ ‚€ ,™¿Œ®y\£“GO Á!Üp3”³³˜€Èž>s©‘3A‚_)ztÛÁºvJŸ?&ä©áRƒ¦%N”€.]ºFÍš÷4ë’% КUÓÈóú~Ú´a¶¡ ÿñãGêÜu0®î·ßRÓÚÕÓéÖt`ï ªU³"§Ã+¿h‰Zôôés£ÙÙs—ÓíÛ÷ù<{öŒ´Ç})=ºœŽYG®}Úùví>D‹¯5Îå@A@A@A@A l#Yæ5ËÖ1 ÞÅ‚")ã€{a&dú†ûýèܹK(øð/]º|ÆŽŸÍºåúb»6 õ¡·½‡Ç ª^­Íš1Œ Ó¢­d麆W{•ʿӒEã ½õ$IR™Ò…Y#~ÚôÅ\¤u;W:qt½M]uÜ«=îK(GŽLºzJ“&ÈŸƒò¬JÿýAgÞî•ËpœÏ™»‚Ó*½wåZ3>±ÒpÏ—/;E™¦ÏXÂÞîc”Ç:$b`»÷á=þtlߘ ÊÅçqãÆ&ðž·î҆cçjÜÈ…å ‚€ ‚€ ‚€ ‚@ØF`ë†íôòÅKÄû Qý'%’ÄÁáS!î¾ ap5z&˽@òEo¹ó9SƒF-ÈöŒÓ*¯õ:>Þ x¶Ï™5‚l‡Ç¹û®ƒ\Zí#‡÷4Èv]ð<¨‹|ôìÙKtòäy}Ùb_µÊïd»¾˜)S:EvW×§4körãxüņÇûÀþ ²ÝÈ @°ëÏÁ¤É Ra‰Åç=þ kyŒ÷é£S¼-]<ÁÈ+‚€ ‚€ ‚€ ‚€ a¥ó¾zµ¿~õš¶¬ß¶ F£ÂÝ7Ãìáî€êC]•6«EG­± Ó­Ù¬IMoC/]¾fdƒ'yÒ¤ sóÁÏ?Ç`Owïz[V®l1[ÉœVÓåãÚÕkžÆñù Æ1HuHÖXo—U{‰¾ë¯_¿!LÀ@ðkƒÎ¼SÚ¢¬éÞ½ç0‚¼Í³g/ôeÙ ‚€ ‚€ ‚€ ‚€ ŒÀ£‡i×ö=h˜ x‹ r舤Œƒ!מÍn&ØV"9¥JF~KC2¤¦Ø±mJ5J—.•ù”¡ß®-eŠ$úÐæ>eʯ×ÍD½9sR%A㓙˃0‡ž<¼êÍ}¨Z½¥OÅ-ÒdYxå#Ø*ôìàõر³¼á:>; æ¤^=ZQÉ‘$&‚€ ‚€ ‚€ ‚€ ÆX±p%óHföìÜGï?¤x ♓å8ÂÝ ‹‡ûWP«(R¹ƒÒ'÷¯™åWt]æÉŒoan¾n.§ëúÖ>|ø¯ÿ* Û±¡žÏŸÿ3ŠfÉ’žIx#ÁŽƒŽšp`UE…Nûá#§éÍ›·\}Þ·ïo“'¤æÖ¶£FÉ"‚€ ‚€ ‚€ ‚€ šXºà«Ü±'9—+"¾m×Ö:IöA„ÀW1ˆ:Ú›ýr7´cò=ãûá‡ÿy+föz¿~ã¶·ëæ„ë׿^O›&¥ù’q|ãæm*\8·qn>¸uëžq y˜Ÿ~ú‰ÏS¥JJ/^åãK'QÊ”I|öÄ‹—:ulÊHö J¦fÛöý4fÜ,zðà1WÓ¶}ªW·²¡EooÝ’OA@‚7.¿JIÉ]¼x>zB±”ž“SrJ­6ÈÏÅŠ3HðHõi¯r€ÅŒJ• šUw>| í;öÓæ-{èÎôJÉô%Qêñ,–=[F*_®˜Ÿ‚ØÔ8žG÷8Î=Ž+•(^ õ^º*‚€ „>N?C¯ØØ’ùË…p·‰Là& áî¼ÍÞÔ¨^ªü‚@º´© ,>ÅAG“'Ol¤éƒçÏ_ÒÖm{õ)¥Kk›p_½f5lPÍÈg>Xµf‹qšÊDª§I Ü=í#ávÈÆÀ@œúô™FŒœÎ瘔éѽ|Å1ÈbkP¿ %MQ€þùç½Êÿ‰Nœ8O… åâ2òGA@B6¯T`«FMºÑšµ>·êÒmµo×:ªÕ‚1bD’Ÿ;™jÔjÃmgÊ”6H÷#j ‹êÃ;÷-08h:K«žïz÷lÍ+MÉrh˜T¹{÷!ç„Ôc²d‰,J:}Áø dÏžQw täDA@|–Ìÿ,Õºõk×èäÑS”=w6ëKrˆHÐT€m&ÜÅÃÝ©AR‹ËÏgX6Ó¹ë`&¦Í-â^ )HkXæÌé/ ¶lÓæ]ì9e}íÚ5Oš2u‘‘\·Ž³ÍãAƒ'ÒÛ·ïŒkú`ÊÔ…T·~Gjúgš=wýøãvêôEÔoÀXrí?†ví>¬³û8qbYèÛ'Hð‹qMA@A ä"pòäyÊ–³‚¯d;F™¹ƒ&P‘â5m>c„\ìïùè13©PQod»u R_§^êã:Êú’œ ×RÎ<yëÛoô7rËeA@A@J°êoͲu¾vÁ7BÞׂr1ÀÂ=À ”Š‚ñc]Ù3mÃC¬lùF´nýº}ûžZr¼›Èt欯3Çõ§páÂùØÕò4¦AnéÔ© äáqƒfÌ\J W'xÉë¥ËµkU4ÊWªXŠŠÎÃçÈŸ;Ÿ3­Z½…XúëxéëÔÅÍÈßIi¶k+Yâër\¼ 6…Ž=C?%xì×oØIy=àì˜\H•*™.*{A@A@¡@B&_Áª¼2Cˆ%2õé݆¶oO¯_œ§§NÑ®‹©XÑ|ÆÏž½D5k·ã2Fb8èÕg¤…CE¶lhâøþtæä&ú÷Ÿ+tûæAš7gÁ»]›ÛàI4múb}*û@ i’„Kñ„ªVþ=j”*A@Aà{غa;½|áÅQùTù÷ï½O}Ê#éŽE@$e€¯x¸;Tª„Žûô©ƒ©u[Wúûïh§ûÞ¬³Ã«|ˆ[ÊŸ?‡õ%ã¼Aýª4wÞJ‚g-ïè–.˜;š=ÔBê`Ú7ªTåOºtéoÕ\Z™/ǃt¢Ê*ˆ¬¶±£ûÒ±ãg™Øúô9õì=B_²ØGÎc‘&'‚€ ‚€ 2èÐi}üø‘;Õl›7̦92Y “ùØš·ìEÓg,ákX‰g‚ åKXä ­'7oÞ¡Q£gÃ+]ª­\>™'(t"âê`åaõjeé÷r iÏÞ#| R<æ‹!‚Î*{ !CšGx࣠"¡ü2mò`›Á/Îb«Ï’–³gÏæËŃ1½¤|ù<­¨°óëׯTºtiÛ³eËF£cÅŠ¥þ¿ï§%K–¨™¶—¨ZµjtêÔ)õ€.!U®\Y}¶õêU®‹/nÔã}u¤tíÚ•Ê—/OQ£Fu¤yn³qãFÚ±c‡ ÔÊ.‚{~'eðB pX±h%!‡¡#e÷ö½ôðþCŠßòÁ¼#ÇJÿÁÝÿ ½íAÝÿH @°ŽAD9^~-øÂT°`.?ŽÞ)’ðË7À¿5kÖôüòÍqÒV! „€ù{æÌ%c°Â)HÍ"©ùìµë¶§={˜«uä AÒÕ›vÑÊåS(©Š86ä›)[¡‰‘\Þ¼Qõuëw š5<Ä\ó>óúŠ•›¨Yó³õ~ˆ°°ý;{î2‹·¡B9öÕã„Ê¥£K”(‘¨^ÝJzÓÛåÔɃ¼Ýb–BS·†->!Þßá#¦©¨ü/4z䯺ÚX:ãÜ[¶î¡ê5[[<ÔÐ'?a.uîjßö ºtD÷Ôï‘Ã{ëܲ„%cÛöýl~Ž GQ®¼UØÞ±‹)Ÿ‘õÉýÊÖºŸ°}çÎú믿”ÅRjÒ¤ Mž<™æÌ™ãEp‡{”õë׳• Ö‹+Æ–+9sæ¤gÏž¢åÛ´iC#FŒÀn£ ‚{† háÂ…F½£+=¢_~ù…¦M›æè!ÒN! ¼!à›d¨øü‡è;ôhçM²ËU{4íª³Ó~U ‘! „€B@!`¸|ù†…Pš5KzcŸ_Wºtlˆí˜Y‡ˆô¿m ³§·ÒÄñ¿'Hÿs+K‘3êZµýÕIa‡‚cΜÚÂÇ#º}B(¶W®]»EHü+>D¼þÒ³5Ûì]¿²—æÎiXÊL›¾˜úôm¯/õç/\1êÕ^ìÎkÃÆ]xâFå½Ù˜3§µ°7~Ý»÷Ð?VœunØì b9~ü8T§vEž9»œÏ,¶ÃÆ~÷wo¢KçwÐÀ] (cÇÍQݯy}”ÞaÕ{F];uxU(_\WÛ]"‡>[°¹A)Y¢­Y5n^ÛG`¤óáóÖ­Ç:pà¸Ý¾üÂÖngÁ|<Ð!¤TªT‰Z´hÁW«½ËÍ—~óæMcóÉ“'Æ:V`ç2tèPêСÅŒÓbŸ36Š)Â3kf̘A‡yZiy×÷¾}û¨V­Z”&Mõ€0)U©R…Ö¬YcóDçW¯^'NÌÑù 64.Xðþý{êÝ»7ûÜ#’Þñ°Ò±.GŽ¡:uêP²dÉ(FŒ”9sfúí·ßØÞº­ÞÞ»w/Ï 8}Úc¶È”)Sx3tùðáõë×­{âÆKyòä¡N:ñÃÝF–B@ŸœÊW°ºXßpRùÕk¶èÔi‹• …ǘ"FŒ òÊ,Qâ’ç AŸ/ovŽL¶7u*„T”9³FP£†ÕŒó#š¾`œ”1KiúøñMž²ztk©,j|¶–€= .I“XFåëúׯß|¾ḭ́ÔÅYc…ÕΡý«øaúvkV›™&N–Ÿ<Àâýf_ug–4µj–§Ù3‡[$5ÛS8ÄçjñÂq¶6¿önG+oý5k·²H{àà *W¶(PïÊÅ‹×y‰ ÆuHh×tè4À°©^­,-]<Þ°/Jœ8•)]˜Ú´ëKÓg,áCÚuìOÇüi<Ðý`é¶æãCÊ:>csçÎåË­W¯žšÙ›…Ò¥KÇîHŒjöV‡¨Ž™%°ùÈܧOéµÀŽèsW•¬Y³²½ÊèÑ£©U«VÊêè„ñÙ°uÎQ£FQÏž=ù3 Á;|øð´nÝ:~!úQüº?~œm[>~üÈí2fÌHÚÒâ¶¹<~ü˜`‡söìY‚Ø(Q"Ú¼y3­]»–Ð΋›%J<æ3eÊÄ‚û™3ghÀ€´k×.Ú½{·Í¼ÏŸ?§cÇŽ©ü²ðçD= ,zŠ-Êö=?þø#Ï€ß>Ä}<$Ùºu+ ûÜX~! ¼!à›èvÝÍÕKWéÄ‘¿){îlºJ–D@"Ü]:¤{¸»©t)„€B@ MàÑ#ÏèRXÉA$÷O™5g¹qxÃU-Äv½#]º”þé³f{ƒDóº4mRÃBl×õHœjÏÎb÷º?ÿâ¦HŒiÛõñÈcós‹º¼‰(xˆîŽ”G&/z{=lt²f/o÷U°H-ãTÎ+üäùo.˜`¶3¼q㎱ۙçFd;fàóc.ùóå -›æókÛæb»n÷æÍ;½J/_zƒF…Wpm;và£1ÃaÔˆÞ^U ÔÝ`vúô›óèÄ·lý8ì Øž={TΨë,ߥn]ÿgÚ×]_d’$IXPÆûk777Š;¶JŠìáç‘Ù•b5nwww7Î3‡…õ9!@#ã„ë;þ¼|ùR7ã%ú…ØÞºuk•høŽÊ[p”ûF¤;€'ÊâÅ‹YlGß/ÄxD©ãÆaŽXçþû(üû÷ï¾í°åÁv… ¸E÷îÝYl/P ‹ïç1fDò?|øFˆ~`&*ëB@Ø"€‡k–[æ ´ÕÎV_„z[ýHïˆàî;^µ6ÿÁ”w‡I#! „€B@kHð® Ägmë¡ë|»4GÌ×­SÑîáu•õˆ..zFŽ_¼äq_»¦‡0¤Û™—ˆP¶UΞóô£Oœ(>[…À.Äú-jãðÓîŒuïVR™XݹóÀ»¦ísæX3gNkóœñâÆ6êaû¢‹3ÏýsóºôãátׯVJ•,ȯԩ“sýýû8¹ëÔi‹¨B%7žá`à¤óç)_¾ì„Ĭ¶ H˜?GæÏ®¹½oÙš IëZT¯Q£†‘LX î`q×Ì£cÇŽ»“víÚ©Pô®½y„z<¤”°ÄRÆÅ¼Epw1`é^! „€A€@z%ŠšËÍ›÷T4fds•ÍõÛ·ï)Ÿvhôï¿ÿŽê׫Âí´ 6R$ObóXT"Ê\$Î|ÿþƒòDCW®ÜÔÕv…R4H–4‘ÑμrÏú¦Í»/ŸÊ‹¯|jÂûͬnÞºkó˜,ÊþþéærCE\êi;¡÷9s¬­¢Ëõ9ìÝó;óÜÖí•gÏ^Ðü«håêÍ*š÷²òœv€j>?üÛuIaúœé:ó2E ÏÏ¡Y¨7·ñ-[ó±!eîªU«ør£Gn¬£"~üøYä©Ã‡·@‹-|ߺu‹-U`?³sçNÚ²e ! Qä®(•+Wf$f…x¾d‰‡½ù\ˆpGõ‹uAÝ!CØ2û.^¼ÈMàÝŽæR¸paÞ!’£œ;wŽ-jðÿb¾¹ Y, ’¢àÆoøB… QäÈ‘)W®\TªT)žÞ¾-úºâŋǶ?æã£FÊ3 î£fHB@Ø#·`:àî1#Çܦqftõ²GEŽ<9hü ‡ˆæ6X·wŸbÝN¶G@wç±4z2G¸•²"„€B@!b $RQà°"àò×öý6$öÀ,\´†úöûaÝ¢ws{ïî=­÷á ì?~øá{QêÑK˜0¡ÍÝY¬Ûóo7GqÇŽ“½¿-´±KGŠYX†¨{÷îö÷6 pø§›Ë²åë͛ƺ+ÇjœÄΊ3ÏHùÇÛ*x(S¨hm=|ߨ †iT´;’ÎV(WŒ“ÖîÞsØØïŒó—wëÏ™uÿæýæã¬Ûɶ÷èS{”#á©­²`Áqò|êÜ©™Í䑿ËÙ¹ë±Y c=]ºÊVá4o_»~K‰E }æ•ë×=-`ù¡-Iù~þ¼G”:<ÇcÅŠa>ÌX¿nò#7*ÕJêÔÉŒM\×ÂùŒJ¬äWö$úáëÉSÒÐÁ=|ìñø‰36Û¸r¬6Ohªtæ¹1ÃÁVù¹UoClG2ÔÓ†(ÑÑòá²8»˜Œ\3}ÎlçÚ5ÏÏaÚ4–QɶÚKmÐQàŽhns% ’¢ÂreÓ¦MUŽä +W®d›-¸›A°\Áï'Ò®úîŠhtXÊ ’¾K—.æ!ðzš4iXІõMƒ ,ö£mPtT;’“"’][Ê`,d̶5©RyOâ7lØ€&FÁÃDXÅèÿHnŠß7-Z´àhwð€u ’Ð"}„ ¾~(¡ÇŒ1½xñ‚¢E‹fœç‚·<Šngì”! „€òœç䑸ÿ¬Ÿžû¿GéA! „€B ¨>´§á!|çÎ}7ÞC<³w][·í¥]»=÷Â…rMÓ¥õ´Y²ôO£ÞzeÉ2Ï}æcR¦ð´¡Yµf‹õaÆöRÓñF¥ZAô´.GfÛ½m^BÔŸ­¼âuäÈ)ó.»ëqTWn-ýÓgÀæáº±mkv9ð+·U\9V[ç3×¹úÜ úÛ8åì™Ã¼ˆíØùܾ¯FÇ6VÌŸ§Cj æd±ææÏŸ¿¤-[=§Á§SQ÷R|Oâ,`D·wëÖÍâOr-ÂkŸw½ š5kÖXœb5¢¹QòçÏï2±]Ÿþê™3g¦Çë*#ê\'Ÿ!¤ëòüùsö^ǶnÁB:Ưý×u{XÏ|þüYoR²dɸ-’”Z[Ù@X‡Xß³gOn_§NNðÚ£‡ÇÃ=<|È;7Õ¬Y“÷û䬅{ƒ˜5kÖ¬ìO?pà@îGÿ€ÿ<’«Æ—räð|˜ª÷ËR! ‚6Ü]üþ¹*JÀÅÖB À—Ûm۶ѬY³”o³§ïr0ºD¹!$ ¡e›ÖžÑ›Ý{¥ßN°9v˜n-<„ 4È;+Õ¨^ÖhëÖ´–±¾xÉ:B{ërîÜeè{ H@IDAT%B/6ª›»Õ6Ö›™ŽŸ>c)]¾ìUÐÞ¼e·…Pj¬V2dH­„¤ô\uåÊ š;o…y7¯ÃG¼b•æÔ¢e/~½~óÆK{ݺ¶  âòî—/_SÂ5é˜ömŒ½VÝvÊjÃvB0WÕÖ˜t«ÏýêÕïß;l}÷8sæ":u^ÉËöBº¼~ýV¯ú¸ÄŒ‰âÅòs;ÿÝz ¡þùÇâ8"ýÒ{¸ñÞdÉ’N%¢ÌdÑF6#€HtˆÖ3f´yPýúõ¹îˆt‡mL±bÅ8r»ZµjìÞ°aCÂ:‡îÚµ‹½Ê}îê*T(Nrjë3Z¥J*S¦ GçÉ“‡“Ÿâ<Ô/_¾¬~ßd :ðøá¶ÌÁF¿~ý¨zõê4hÐ ä!ÀGŠɸ´EÎÙ¨Q#ŽV‡ð]ºtiÂlX±b‰VëÕ«ÇÇM:•j×®ÍIeqÎQ£FññZx7:·Z¸Ž2bÄjÞ¼¹ñpdÒ¤Iü I[q+ú×É`Ñ¿-{«îeS! ‚Ï»« 6ðÀ<\‰pÌM!4 `*rÇŽÙÿÔ·W€D\ør‰h®Å‹=Å7ßö#í…€ð?þ};*±,ÑÑo¿£tKÐÏ­z±h¨÷ú ;±'÷ýû¸ìUÌma?S @NªW·2ïGDg‰Ò hÔè™,¬BhŸƒHN »Dµãÿ{ºté,„vó1ˆÒÇ ÷5J@<8!‚ߺ”*UJýž»ÁQÿ°¬§|üøñ­›±Ç<‚ÐÉfa÷‚™Ž|§Ç¬ƒ‹/ò=€».9sæ$xÑÃc÷s°ºñ‰½>V–B@!4 H„{Ð|ßdÔB@! +zôèüÅÏ·—Ž/‹Y²dá/xfoTݾ"ú_ ,ÈÑðH®†é×HÀf~¬‘¥~'€Ä¥À‘h4W®,*"5Œ—ÎP׺U%ŠþEeJ[&GÔ‘ètçö%4zä¯J¼‰©«eôèQ©oŸöthÿ*M™À¨×+ˆ´?qt=Õ¬QÎH¦ª÷!RBjû¶u•Í%úß³s™EÔ¾nˆèôöíÓÁ}«”—q<]í«%Æxìð:j×¶‘=}çÍÀ¶e˦ùÔµK Ê—7›y—ź«Çjq2« Wž»Eó:4qüo6ßfΜ–?#'o¤˜1£[ÊcÖ=KS_[ìwDdÄM× S'6R±¢ùŒhwÝ,=òäù‰œtéÜ\WËò@ä;lÌxC¤y`|1+$mÚ´nŽj·‡ ‘âðIw¤-tXÖØÛuÿþ©ŽÈt<”pôÿ>¾ðf±]×c‰"ð…¬ìÍc•u! „€ð‰p÷?þî;›Hƒ €h¨Òå©è«ÿÑÞ]ËÙ5N+§B@8¾$>{öÌ×ýÁOSÊQð¥ÓÉ­‹››OkÆîºuërûÍ›7óôk×®)KŠ×v¿£þ¯E¦LJdN•*©CÖƒ:wrã×£GOèÔé l'‘5KzÃÝ»E™–/ÄÏHpzYÙ¤M“‚#—!|¡üûÅ«¿»¹OD-»ŸÜ¬fÒ¼U³p.Òý)©øÓªä˜‘"E47õÓ:Äÿ ã~ãc_¼xÅœ®^½¥D±ä„ëĬ]êÖ©DxÙ+~ë«ûìugÔÿ±l’±noÅUçÆùÚ¶iDnÍj+þ8Ê?Žz“6mre¹ËÎÕK»8Á*¢ØsXù¨Ãn¯?Ñ“'Ï(bÄWÌ4ðé32eRÚ¾m–ÏŸ¿JW®Þ dI±×¿¶2bZq[S—²*„€B@oN@w¿¾}"îâá„ØîsæÌ¬|@³Ïç;•Ñ^ŠB (øôéOG†g©wåíÛ·-ñYFbT¼¤! „€B ¤Á=¤¾ó!ìº1u»Há ½†ÙlNÛÆ´Ú W“û™‹*ªô5O±®_¯²òAöªô@´l÷žÃ´wßQ¶cȨüDŸ>}¡"°òsÿº,…€¾!.\8ömÿé§Ÿ8×Â… >ìö÷ß~ûN:EK–,a{}®ž={²éÙ³g9êÝ,¸Oœ8‘ªW¯ná®Ã ÄÆg®’u!à#ˆéZ@ÇR‹èºÎ,¦ûØ™jpü„»#ͤB@! „€Bà›ÁÝÉø%±œW 7í¢eË×s²¬iÓ—Ðæ-»9© üJ‘”fðÀ®œä G¢î›èúÛ*ªí6eWþ’S§-¢+7ññwï> ¿Ož£}»ÿà}ýú•~nÕ›æÎ[ÁS 3«iÐû£¡Ã¦°È½bÙdŠ/¶Å Ö®ÛFU«·¢OŸ>s’2øæÎ•–,o!¤Ÿ:užª×j­²Ôßá„f‰Ƨ¡Ã§Ða“iòÄß©i“šF¿ËÿØ@uëwàdQH uûö=êÜuGå½~óFwƒ”¬àK>ç»wïæÿ÷HnZ¸pa›ò|KÂúìÙ³ù°ܳgÏ÷Ê2 m‘ø^ìæ9rd2dUªT‰{$EÕ¥xñâý~ðàAåÁü† òGˆ×[µjEäù傼!Ö‘éˆJ‡®…u,udº³ˆÙzØí¬¾¥! „€B@! „€³ˆàî,’vú‘w¢©“q¦ù V)ÿàðœT+£J†ˆðî=‡ò _¢‘¨ ¾Û¶,PQp·(UÚ¢Ô¤Y7J“:9]¹¸Ke‰OÂõŸ?1h:…Åö‰ããdQz¢èK—kL›v£­›ç[DºCôŸ:y 5nT#Ñwí>D ufqý衵\‡dRµëµWbÓ;ºym%Nœ€»~÷î=U¬ÜœZµéÃIºà·Š2qò|*\(7'‹â õ£÷/m©LùÆ;)B@_[´hAë֭㇆úJ“%KFcÇŽ¥Ê•+ë*/‘ô¾ì¶ÊóçÏÕÃÄO”>½å, Ý6gΜ¼j}ü¡C‡èòåËÔ¸qcÝÔXV«VLV5"¸X‚õŠudº¶|1/-¦›âáP„ ÙöË#Gލ„¢gy–™¹¬ ! „€B@! #Üü®H„»} ž×¬œ®d^ Ä¥óFÓ™³—hÀÀ ,¸[[%~Z8 GÂcDws3n•-SÄBlÇ~XÄôýµ=õì5ŒÜÝ/Z$nêÔ±)[Õè~ŠÉ˪Tû™þ\¿Ç(ü+WnÐÌéCy¼ZX: è߉ «CËWl0’›EQI¨Ž=E[·í¥”jŒÑ£GU:)èÔ‰Ml£Ï%K! ‚ø¦#ÊÑåçÏŸ§©S§*««äææFE‹5.âÙ³gj†LnŽê…XݹsgÞwøðaŽGâÑ?ÿü“Ê”)c㟕øñãóƒIXÊØ*ð\G÷µ­‚ñ¢X îˆÌïÝ»7_k¯^½(jÔ¨œX¾î‹/&x¼§J•ÊV—RÄX‹é:2]/}kóâÛË·ÓamQ] ìÉ“'çÿ{æ~kժł»¹NÖ…€B@! „€•€îN~g¬w‰p÷œ)SCl×µ¡B…bóÉSrd("ÜÍ¥S‡f†Øn®Çú;÷Ub¿7T ë]¼­ë῞%K:£Mñbùu½R¼X>^=í~w<@iѲ¿xÃê‡9yÛð¡=©\ÅfT¶|£Uøð?R¡‚¹häð^”!C$£^V„€xïÞ½£}ûöq2Ðĉ«‡ry-ñÇÐÉ“')Z´hM»zõjž3pà@ŽRûö-GޝX±B夸Àžêè`ذaìK=cÆ Žr×"¢5|Ñûõëç4Á‚:JkÁ\Ÿ‚|† hÛ¶mÊ놲ÉJ¦wñ‘ê(Ö‚½ös1b3†¢D‰Bÿþû/?dȘ1#-Z´È‹ÊÉ@EÀ'1Ÿ Ì€pUñ‹˜îª±H¿B@àH3Ò'NªP¡‚·—è][ØrΟ?ŸðÝ5þü*P(­Í¾–-[F¸‡Ê‘#¥L™’°­K¹rå”ug<½i±L˜Ð¾ö:Ž- w÷üÅK[Ý’®#ªÅþ/^YlcÂ=JäHy)b^.Y4žüÔº Z?I›ìƒEΫ{éàÁtGùÌ?þ’.]¾Á W«ÕlM—Îï°îB¶…€[¶l¡áÇ«ÿ›-ü¤ñ%‘ç?üðýóÏ?Ô£Gºÿ>?øK‘"õíÛ—“ŽöéÓ‡Åu|i…]Kž&²²u1—é3–rRÔ*ñª¹LžºjÕ,oq¾ISpD¤£äÊ™…—;v¤:µ½Þ >{ö‚Þ¾}¯¢*"s»…kR§M©Fõr¼­|÷Ñ¤É èÇâ审ÈR8À‡v'!a邨në‚mXQüøãüe¬víÚüe"3"´fÍšE-[¶ä¼ È!®<˜V­Z¥fÅdá/ˆåË—§bÅŠv*ã! ë›ÌÔA4;„{”°aÃr:„qñHnê,±i±bÅ¢ãÇÉSµøŽëÂïQXÛÀ×ÿu]ÀaÍš5Ì _°$HÀ‰Q±ÞðçÎÓMeÀDL`àr:! „€à{$Ülذïap?c.˜í‡‚ˆs$ZG`‚¹,]ºÔ¦àŽ{-¶›Ûûu½N:3öpa÷-]»våè|RHB@!˜XþU L# &c‘wÏ7yÍÚméçu駬Tè]N6 ‘jüØ~ÜðÕ«×tèðIN¨ŠŠƒ‡NЋ—éñâÆ6<Óu¯3¦¡¼ªS‘âuiØž”IEšßUæ#GϤ wи1ý(vì˜<-ñð‘“|Ø‘#§y ·:*’óGÕn'-^²–Zþ\OEszí•ðñ|á¢5ÊV‚Øf&‰Jœºoÿ1Ú¾ã­ûó/jß®1Ñ›û¼ÿ‘J¼ÚEYKܦš5Ê©Ó8êFó4?LÈœ9­ˆíú “¥ð#Ds8p€£®!(W©R…´9DæQ£Fþéú¨ß»w/¥I“†Ew]q~íÚµ,ЬYÓÓݨßociþ‚Uœø/¾ììqž:ur>ðÏõ;¨qÓ®F'Ý{5ÖÓ¥KIçÜ·ÛXÉ®¢×í_Eí:ö§2åû%ŠOKO Úµ<¼ sW‚xgÞ?nL_Ú´y7ýܪ7'„5Íð¡¿P—ÎnÆñX™?w <‘ÆŽ›M³f{Ü8¢ç„/{ËŸëb“KäÈ•¿á:|*õúu×ÅŒJ•,Hýûvø¯•,„€ð+Xà ²â1<= .Ì60+‡Êëøâ¨½Èõy Dcš/îß¿Ÿ-9ô—Fëˆn×(‹áS Kد<|øPwGå‘`ý *Dmø›Zû£ ¾ŒÂç½iÓ¦lÛ‚:¿”˜1cr?ƈÆ%êí<p…È ‘òÖ¢º~€Hzx«ê1"Él¾|y2ì5¨Ö1][ºè¤£æ¥Ø¼ÕO–Œ[!àzøYxˆrÇßu³àþàÁ¶ãÃ(an]*UªÄ÷?°­±ܵϻnc}¬3¶1›–wÜu†wý¾zõŠ—ƒœ@Ô>f1ÂŽ÷zº`æâ¸qãhݺuD€‡˜õˆÀkÑ_coùùóg=z4ÏœDîp_Ú­[7ÒAöŽ•z! „€úDpwñ{èÌ(G5@º/P 'íܾÄî¹6¨Jxù¦xˆî«•Gò[e£pGE—Æâ¨vs8ï—WŒª6­ªˆ‹O쵎ht[>íCu§º¨äƒwT”ügömG¤¾uÙ¶yÁÒ&lØ0ÊÇù¾ßÞ«é)¼DƒX'ÛB@8FÓñ¨sçÎ,^Ã>EßÍš5ãD¡ˆ‚2x{4ˆ~ÂëíÐ~Μ9,Äã!©þ=­w|)Òu–uÁ6Äõ§OŸ²Ø/jð‚ÿõ×_9’é½uëVš;w.a & µjÕJwáë%’–ê/¯¾>ØàK+«‚^ˆ*ÃR ìHަ™ºàôÖ¥-1Ý¡‘é"¦ØÛ-'òa*Ø`ÿãÔ¹S3²•˜Þ]ûxè•+7hï¾£7n, ËB B Ö¬ÝÊ÷«%ŠàûÐ[·î©Ù–ûUÂÆ¨TµJé@s%;w¤0¡C+둜fL2Ÿ Àz‚;fþM˜0Áøû ÷HøÛë>ëÒ AÜq‚Y†:˜‰åÝÝÝ)uêÔœCA ®(˜í·sçNî‘îÞÜÇ/^œíö`ˇ{–S§NÑéÓj6²Šâ‡¨¸¿C»“'Orîí_¾|¡7?Ö¯_ï«Y‚˜Yˆca;ˆ1^»vû€%"îkK—<ÿ½ã'û„€BÀoDp÷7»GI„»]4.ßI%<Í’%Ãç .,[¿øt’ ¥L™ÔÛf°­ÑÅœLU×ÉRÿÀ$D$™ zµhтΟ?o®æ/P¿ÿþ;Ázeòäɱ® Á)‡"‚ QN(ˆÊ†Pl-¸Ÿ9sFÆ_<±6ˆ.Ç—PDwýüóÏT¤HöoG˜B ¡IÀz÷îMˆ:.IP¼®À$¦ÛÕ%2=(~ª‚ߘ¯^½©,úfP”(‘hİ^6/ƒ‡NVI©¿ðÌ@›\X ¡¿EË^jQÎ`+¸ÿöûxõ;÷"­Z1•÷¿OžåkΔ)m ܧN[̹DpwáÞ]k[ÌRƒ= ’¥£hÿvò¶¢CŒF@lïvíÚÅB5ŽÓv2õë×ǦSÊ‘#GØTw†{°+Vpðô¶mÛê]6—¿üò ‹í•+Wæ{6$´ÇŒI\ë¥K—øA’¿"Xb;’±âÁ¾?DNäùY¸p¡Ã‚;fGBlǃœÁ(áñ0)Dp·ùvI¥B ØÁÝÉo¥µà®t—_ÆO˜ËÑGoÞ¼£:õÚóM¬^ªT.âÙ! 'TEY¡ É\0Ý @Í‘èØ+DÅÃF—÷ïßsd¶qŒþB…~à×nîçÜ´iG±ã‹Q²dɸ|a‚] "ê«V­ÊÖ2øò‡/XˆºÂ4g$sV²T=vYúLÀ;1êxáýꪂ/êZD×K±yqméו>|B3g-ãèq{‚ûŠ•›xVòÞĈ͕Ѿ)’¥RÉTRòW "ð!€W¯^£ÒuÂ{ë“`<ˆƒ8oÑÑ ôƒlãáÄ{XÇà!ŽÕׄý¾)xøŽ„´ÈãkCDɧI“†óùÀßì—ï›~¥­B@"¸;ù½RÏ-JH—ÍztoiÞ ±ëáÃÿÈ_J?žÇÔCGÛK;!Ü  ‘êU­ DsD)™ <Û­1/X¼ "Ϲ?wDCG‹æ•9mÚ4¶~x„£øò†è-ˆïø’†ó£L:Õ|§¬¿~ýš¯  ðÂ5êuDØ{—Õ)ˆ˜ÞB'ð×ö}4yÊB:í~^¿~«~wÅ¡JKPOu/†\3º9rŠÆŽŸMGŽžâv Ä¥jUKS·.-ÔïÙÔ¦]_rW6&(ÏŸ¿¢ÊU[(Û­4qüo\‡—/_§ÊÒÇ"I¼.¯^½VIç'ÑÁC'TrÂ[j ±Ùî¥K'7/Qðû”ÿúÄÉ x¼o³fIOU÷b•‘mÜø9´tùzΫ“,Y"jáV‡B…²-è¾ÿ ™D»vRâÜÊ™#3aveÚõ0åõÐ`é²õtîüeJ—6%{Ñ?yòœÖ®ÛFeË¡V-=­1vï9Ì|?Í×R êýK%ÒÅ2úƒÅÍãÇOiêäA´jõZöÇzegq›Ò§KEýútPI¿½úaûaEG”#ʼ]ÛÆ=ôúu„²[»BÍÝjSÅ %ØŠc;º/MŸ¹„þX±‘ÅÈ"…óÐø±ý”Hþ/õî3Rq;¬þ6F¦ª•KS£†ÕT~$Û÷¤H\Ù¯o5›+ 8ó#õèÖÒ&g‹ÉF " me.^¼¨œ(û"åkŽâ]t;öã3r×àž ÷*°~AÁ½“3 ú³NXÚ½{wJŸ>='M…øÞ°aC›§Ä}Š91ªÍ†ªžî8‚2ð°Aè¹kFŽiï0»õC‡¥Š+rÔÿ¾}ûØŽ|gΜÉ68“&M²{¬ìB@ O@w¿‡"¸»pê>Z´(Ô¶DÀ¡·L† @X·Žd‡u ¢ð þî;v¤R¥Jñ´aøŠBHG/æˆT‡×:¢îà­i.H e«À£ÔY_¸à‘jÕ!®ã !þ^ *×Ñ‘VE‹5¦2;k ߢ-¦ëhtëu0h›™Ž%˜Kdú·ødÈ9I`ñ’µÔ°qî2V¬”!Cjöþ:l Ï÷yˆhû÷£Ê 3fLÃÂñ™3—èw%˜B Þ½c9{ɈXÆÌŸcÇÝY˜7wîü•¼Ù¤qu#aâÍ›w©x©ú,ŠÇŒ=ÇO:¯~+oõfÂìeÔè™Ô³×°ÿ3&T¿³¤?×oçWëV hòÄß¹~Ô®ÛžÇzŠIèÇԺm1šU‚rñR èܹËJ©DÁø´yËZ÷ç_Êêä ÑÛh>¦ÏXÂÛ`qíú-ªV£5eΜVÙ’å‡ ºñÄIó©S—ßùïG¦LiÔßœ„ºµëþ¢Íæ*ñ/7EBS$6… Å–­{ÔÜÊ¢ã%³=pðý}l½ÑV÷íŸ%Ʈ߰ƒN*θÏÔß=^¿~CcÆÎf›—1£<,9ôØ(» xôƒ üá,\M7oÝåkºr妚ݕF%•¼@Gž¦uëÿâ÷ÍÞÑOÏ^Ãiîì|-[ÿJ%ŠçWŠ=RÛ;N곭̼yóhûöí<8DbûT †Cp_¼x1'_Åçó®.HÊš?~Ú»w¯zøwÙîé œ£à¾ ùu`¨ f4ÂÁ]»v¥þýûó½d·nÝ,öéÓ§ëC^Âþ÷>)S¦ä™•8÷z˜e‰ó ·¬ õlL‡;–†B@!dˆàîä·ÊÚÃÝÉÝKwB@K ­îß¿ÏÑWÏañ‚/\ˆ::qâG:ዞŽX¨¨Q£²¿h`?SD€AàÍ!? €¸®_æ/‚eÌ>ã[‹éQ¢Dá™ fŸtÓ}z×dp$0|¤‡(ÔµKs9ÜCT†¯všôÅÕïË“‘ž:urZ¼t‹ímZ7¤I0ŠJˆÎ“¿íÛwL%œ>AûvÿAæ ­Í¢õý;‘«šì&æ/ðð›6ö´“ù¥÷pÛU¿dÑxÑŸ?I9óTVç¿A&Í£ßúuR6 W9’âðÒŨVMd؈x/_ɦN[Ä‘ù¥KRbùnÛñû}ÕŠ)T¡¼‡W4¢ùË”k¢‡d,{÷Åb;"Ó'Œë¯¢àCq„yábu”=‹*ªãeŽsAl‡Ð¿móÊ—/;÷1iò|êÐɃ‹îôÚµ[Ô­ÇN º{ÇR%H§å]}ú¦!*ilÛöýh—ª7—S§ÏÓ¥ó;X´úô9eÌR†#ßÿX¹‘˜Ûúg½frÔ¾ãoÊjë ä¹sgåî6mÞÍb{žsæÌ\ ”¶J»ví8Q§ÞçH[îH€ !Å';Ý7þNCdG2PgÛÉèóØZBÀF¹wïž­Ý\‡xˆP¥JŽb?vìÏ Dd;ÄvÜS6oÞœÛÂB–/mڴᇸ¶-[¶p?œ={–ï9 Ž€h™ׯ_g›šÊ•++[¯ÈìÆØŸ8±ÇïN>¹üB@`GÀç;ê`wÉ®½ ‰pw-_é]KÓ•¥8@`Óu4ºu„ºØ¼8ï½–žBÅ (â *:|1]¼t._¹¡lAÎÓÛ·ï,´S¶#KT”;"Îõ)RDNnv·fµ¼VuG³çèd©žÖ—.]çݰqw¼¹@¼ÅKD¸£˜ëô¾âÅò±à~á¢G\ JÑ"yucY®LQ Á62¸GÀKZmvXyöüo_½v“—ç/\áe^n]råÌb!¸Ãreòu?løQ÷ù³‡@yUEÁ›÷T©’b·Q’(|ø¤;»4lP•wØöÀ2]ˆp‡Øïzë’4©ÇkÔcæ>?~"D§ë†âß½{¯˜êZYWf[\£#v2šDvî”!¾TAðÊŽ;”Ñ3»>í•*Uâríß³‚}ǬVÐ׆;9 kêe½/U²T”1¢uöVÞ)K”ô¡ûãËzQ>|øÄK™ÏÿýHž<‘y“^¿ykl[ÁDUE™UãóˆŠýþ{È[ýÖ¢¸wýæÉí!ØG‰É87Vr–kÙ2…™;¼ã;M?e¡×kËKÝÞØÌ6$#†_¼)¾i‹þcÆ»‚ãÖÿ¯ÐÑÛ¶êዎ—O M­·UgÝ"ùñr¤”.]ZÍ.¹I<`?÷dÉ’©Ù–a…?zÝ!t̲9/E! ‚?Üü[ÿ¡ur÷ÒB@„pML·Õ%2=„@åò¿ D¶Cl‡½ˆûÉÍÊr+ž1ŽŽ-ýÈ÷®HŽÙ´ywN” ŸuXÂØ+ ­a;X¼üø£gÂTÊË¢/¢¥­ ’¡®X¹‰²fIG]»´ 4ÊÚæÀãtÚý5¨_U7ã%êPÐ%…²ƒˆ|îüeªIå¸NÿpW ?ÍEçÞðçló.ölþôé³Jðê “,i":tèo:ªú®SÛ2*÷ÈQO›t‚~1†fMj¼ïÍÑäÿþû?eïÆ\ ëòÉ>EY߬TQîHèŠÒ ~•‡œLvð}×ÞïöÆŠû¼Ì%R¤HÊz*Ÿ¹Êáu$F•ä¨ã’†B@`C@w'¿•Ö‚»½'ŸVºB@`@@Äô`ð&Ê%o@àÒe;— âZˆíg” ýèÑS‹Õ©ßž“£Â+}üØþ‰ ßo$ß8h"‹å8@G€ø`™ØoŽòHGøl.)R$QÑ›‰9A)’žvV~ç(œœ°çP•8ðeþ/Ùh…òÅØûÖ4m[7R▇Š’sŽ?‡C”²eŠÐ²åëiÆÌ¥ÔZ%B'×ß¾}´µ W¨É’%â$žW¯ÞdÛœz&ÿò-{±¿{‡öMhܘ~ì k<¨€Ï9|ìQ&OY@çÏ{ØÍp…ú/y´®ÆÐT]·~Ðl9+¨_éÎÍJT‹© ðeCõà‚;l¼yóŽ"FŒÀ‰g9xÐÃâ'²² éÝËҲǙ瑾„€B@!Ô ˆàîâwPw–î…€A„Ätmér÷î]c]×´Í‹ŽL×KDsÁsTŠA¼·qÏ ó •ܨHáÍ‘=3ݹ{߯!¼£$L——¯^½á>ú)=U,_œÅèôéS©„¥Yy¿þÁÉ Gõ¡*Õ~¦®ÝÓ±ãî,ÀC†Øž8q|jîV››W©\ŠEì­ÛöRžüÕXü†ˆ¶×¯ß&ôa¥^ÝJ4jÌLeçp‘rå­Êm!pC„ÇÒ\0†Ñ#{Såª?S£&]é¯íû)y²Ä´ÿÀ1^‡½J§Íø êZ ¤c y T§92ÿ;wpÕƒO]#R|âäùÊrÃræ©LÕª–VžÑ/i­òtlj®]šS±ÅC“T©’1klÃ×=|xçþN_¹j³ò†ßEqãÆÁ¥! „€BÀÜí€ñkµu„»_û‘ã„€B è1=è¼W2R! @h>u0uë1”Qˆ¢HÎ×¥³… õ <‰#´5¬F-®G¯_¿%øµCàÆ Ñéƒ~ïÊåØN¬’|vëÚ‚ÆO˜Ç}Þ¼uWy?Ã./Ñí\©~TªX‚6oœÇ~òÄu)X0'M›<˜¢Eóô.^·fõí?†&M^@&Î㦰FÁÇíÇÉ¿ZŽû¾EiP¯ õ0–O-v2ßâs ! „€BÀƒ€î.þ$–÷ÏŸ¿Ðî=‡]|µÒ}p!póæÝàr)rBÀßÌbºŽF7G¨Kdº¿KB@8@s·:T·N%öa‡‡yÊ”I”¯¸‡:¢Úß«D¥™3§å3uïö3uìЄnܸ«ü¾ŸsÄ2wëûÖÃzÑð¡¿Ð‹¯(R¤|/Y¦ta*V4¯Ý#jüæµý*9ácösO–,¡ac>Ð÷ÐÁ=èòåôùógJ—.¥!´›Û›~õÊiMŽ(~xÔ#Ê¥f ;sûR% Ò«û8‚÷4ˆ®ß2A"’¿ÂnÇ­i-ÍÍÇ÷ì5œ7S¦HjTGR6*sfP‚ü ºpá{ÁãA‡Ù«q^[¥¿Ž„—oËé¿=ˆèãªV)Mÿ~ñzÍØß·O{~é¶ÖK{c{tÿ˜uSÞ~óò¬E½µ/¾ÅNÙB@! „€0ˆàn pÎJ`‹p×þ›¯_¿¡b%ê9ç"¥—Cà‡¾1×*2 1][º$L˜ôº^&I’DY„™oŽ\µ¾&!BxËÜ|pÚ´)Ì›¼Á;Mšäüò²ÓT>zô¨\S²DAÓïWãÅ‹­’Æö¾‘Ú‹{U[ã³u lgr¨(tG Dy-Ì[sòÔy*S®±z“þ>¶ÇŠd²›·ì橸îBsYÆ3²¨ä¯þ-­ÚüêcV§¼y³ùØN! „€B@."¸»øýP÷êß´¤NŒjת@îÊ÷RŠ%/_¾Ðµk·èÿ×½ï8ɦ-Kñ €/툈“"‚*[bº9B= "Óµp.bzPýɸ…€®ŠËÇö9ØS¤.ÌÑÿ×®ÝVÞì/ø’íÝ–œ!¬ÛãW´ˆýYúGXè¶²B@! „€<Dpwò{#Ü—.žàä« ÝmÜ´“*VnÎóï¿ÿ£YÓ‡Q9ƒÇÅÉU`N 0‰é¶Du‰Læ@¹Yÿ¶´ÝŽÅ—þÑ«²BàðNLG„úíÛ·éãÇ.a”(Q¼X»˜#ÔELwzéX! B… EUçW€žXN&„€B@!¬ ˆà¬ß^¹8ïXÛÇÀ·SŠ®%ðüùsBÂQsÒQmóò­Äts„ºˆé®}ÿ¥w! „€B@! „€Á€îN~‡­-e$ÂÝÉ€ØuBЯ_ÿubïÒ•y|Ó!¨øðÁe`lE¦‹˜î2ÜÒ±B@! „€B@!`ƒ€î6 8³JwgÒtn_áî\žÒ[ð& Åtn¡þ-Åtˆê‰'¦ðáÃï7A®N! „€B@%0¨ÏPš0bYWè ädBÀ{vì¥X¡ã;ÐÒ9MÒeLKÛl¡0aÂ8§Ã`Ö‹îN~Cå—°“º°»P*Y–¹ˆ¥Œ™†¬‡$ßZL5*Á#Ýì“®#ÓELIŸD¹V! „€B@!¸lþs‹ˆíë-‘ÑÎ^¤WoRšô©Ɉ×0Dpwñû!î.ìî½F¸‹¥Œ?pÊ¡”€µ˜n¡îêÈtÓéC†%„€B@! „€Ã¬šC9óæt¸½4A J‰êtéüe>U¾Byiö²qZÊ—±½xþ"@ÎTO"‚»“ß9‰pw2PvgíáþÏ?ÿ¸ðlÒµp>À"¦ëhtëu±yqþ{.= ! üKàãÇO.\Xÿv#Ç ! „€Q"G‰L1cÅQ×,ø „ å)ë†:À>£ßÿ]à‡óGèùÎ|ã×ÓK„{à}g%Â=ð¾722"{bºŽP¨ÈtÓåÓ(„@`!0hð$š2uq`NÇãÇOéé³çìµ™4IB2IsÕ!åÞý‡<ß:ØÁUç”~…@`'€ÿ#†§÷ïßö¡Êø„€B@?ÁÝм;D"ܽ£¸öyÜ¿®Êh‚-À&¦[‹ê™l?zraB HHž<9ûá£'„—ÿøüù3]¾rÝÿIB@ø‹@ذoýu¼,„€B@N"¸;ù}±Ü%ÂÝÉ€Øu”ÕׯâáîD¼¾îêîÝ4xèd1ìŠ)¢¯,ˆ˜XÞ ‡Á…À!C¨J•*ôñãÇàrIßä:Þ¾}K+V4ÎíææF 40¶]µróæMjÚ´)ÿm_·&`|E]u-Ò¯pS§ÎS—nƒ(V¬XÎêRúB@! Ü]üfˆàîbÀþè^"Üýω‡Þ¿ÿˆ†ŸJ3g-#DܵoÛ˜Ò§OåÄ38¯+ˆéÚÒåîݻƺ® h›™n^FˆÁy,= ! ï¿ÿžòäÉF´‡ðòåK‹ ÀÌ"EŠXÔ¹bãüùóÜm¸°a©Hay]ÁXú ºðûMŠB@!üˆàîä÷Ô:ÂÝÉÝKwN$ ‚»aú¡«‡ŸÐð‘ÓhÚôÅôéÓg£‡»÷~Á]Ätã-! „€B@! „€B@?ÁÝà=ì;IÜë(ªo*ÔçK .Ûxòä™Ú§«Äw •=À'/ç¹sç¾—:ÿVÀ†àôéÓdŽJ7¯Kdº ËñB@! „€B@! „€ ‚»“?áîd .ìN"Ü]×F×Ïž½ ‘£gÐ¤É èýû6ZxTݽûÐî>[;ž?©¬]ÐÝ{þ[>TºÇúi÷ |Èœ9s/W”¨Q£’¶tI˜0¡±®ë°›W—>…€B@! „€B@!øˆàîâ÷D<Ü] ØÝ['Mý矯þèMµGàÅ‹W4zì,š0q½}ûÎ^3£þŽËu±%¦#¢<¬g°n+J^ïߥˆéþ%(Ç ! „€B@! „€B dÁÝÉï÷ÿþçä¥;—w—¡åŽ_½zMcÇÏ¡±ãæÐ›7o>ÙÚµ[ißþc*¦ëhtëu‰Lwøm“†B@! „€B@! „€Š€îNþX[ÊH„»“;±;ë÷¯_%ÂÝx!®?—ÆŒ›E¯^½ñu—ÏUD<^þ)Q¢DRÖ.ñéã‡tõÚ-Ê•+µjÕÊÂîEÄtÿ–c…€B@! „€B@! lÁÝ© ¼F¸ÿ"®ÛU »˜‰“æÓ¨13 62®*ZLO˜ ®ÐãQÂñ<– ãë"„çÓÿöû8ú}àÊ‘#5mÚÔUC’~…€B@! „€B@! „ÁÝɉpw2P't÷ï¿ÿÒ»wïéë×éŸþQ˯¼psyðà19s‘÷¡ <ݱŒ7%OžØÜTÖMuò”…4bÔtBbTg”ÅóS²d‰ =¡ 1Ýç‘>„€B@! „€B@! „€3 ˆàîLš6úKP¸êÓ§Ï”(i~zýÚ{{“ñç^ÖeÙ’‰"¸[C1mÿ±b#íÜuЩÉKÇéGéÓ§2EV…€B@! „€B@! „@à'ð}àbСu„{Ð}ðí?†£zu+ùéâ"EŠH•*–ðÓ±!å &kÐæóèÅÓSt`ïJô{W*^,?»_ËÝ{ýz¨'„€B@! „€B@! ¾‰pw1z‰pw1`»wkZ‹¦M_ì`kÏf5ª—¥páÂzVÈš]¡B…¢¼y³ñ«w¯¶ôùóg:räíÞs„ví>D‡ÿM˜màH¹sç¾#ͤB@! üEàòåëtêôî#tèPTµJi‹þÖ¬ÝJ_¾ücQg½{ÅÄ*Y{Ò¤ )jÔÈÖ»Ú6û¢GBI“$äsáÞËÙ‹ûöãnq®Å §ðnŸÑȇÌŠ|úÔÂ0¾ì”@åä‘"„€B@àFÀùwiÁ/¯G"Ü} ,€šgÏž‰²dIG§ÿûBåèiÔ¯âhSigE L˜0T°`.~õíÓ^‰íŸ”è~’Å÷Ý{ÓáçÔØ/VGylÞ½+î6ÁH¥B@!àT7í¢®ÝsŸ˜Ùh-¸7iÖÞ¼yëð9sçÎJƒv£bEó9| šÇáÈqâĤn]ZP»¶(lXç‡ü}ò,թמ‡€ûg³àîÝ>GÆŒ6}û¡C‡þææ«WNÁÝQpÒN! „€RÄRÆÅo—D¸»°/ºoÞ¬¶/Z(\(·¯Ž‘Æö àË`‘Âyh@ÿδgçrzùì4ýµu!!‘ñæ(­;wØïHö! „€B Àì¾¥Д© ]:ÂGžR÷žC©EË^.=t.„€B@‡þE@IDAT!à{áî{fÞ!îÞâù¦;ë׫BÝz qØÖ¤~½Êôý÷òLÊUo<ÞáõŽÊ»wïéÀÁ/ÿ\E]úB@! üJ JåR*r=¯—Ãß¼yG§Ý/КµÛŒÙ{ˆš/Qð]Ÿù«…XÊø ŸÓnîæ˜­ üÞñ…CŠB@! „€p„@Ù2E(EŠ$FS÷3ug®à?ý”Áè2°%›ÿüù3:už–-_Oxñì™G’TcÀ®ÜºuOÍØJ'Nœ¡¯_¿:x”g3<˜X±rÁ³VÖ„€B@!àz"¸;™ñÿþçä¥;§€'{òä‰}ìS¢Û}D$ „€B@! ¬$JϨ}ž«ŠÙö°@þœ§I–² }:9¿Ž=e±OoÔªÓÎh3yÊ]­’·î4êsæ©lÔ;²‚™¾ã'Ì¥h1³R¶œ¨^ƒŽñ'~N^ôè‰Ý !C'ŽÁuT¯Ùš0ލ1²P…JntïÞC/}Ì_°Êó¯}Gq’[œ;UÚ¢T»n;öº÷rT! „€BÀ…Dpw!\t-î.ìËîñ~¸5­åíQhS·N%oÛÈN! „€B@!`&±øÊÕ›FU&Í–Dä6¢¾u)[¦°^ý¦ËJUZPç®éÇãø÷ß9Ú½¸J&ûòåk‹}æ/^Q¹ M©O¿ÑôäÉ3ó.Îõ³ió.Ê•· ?în±Ï¼sU¨ìÆç3×˺B@! ’€x¸;™¶µ‡»“»—îœ@ IãÔ·ÿ ¹­‚$žñãDZµKê„€B@! „€ïß ÑcfØÈMS®l/í©øçŸhß¾£^š¾U æÿ\¿V®ÚÌþíáÃÿH³g§D‰â{iЋ¯áèx}ÞjUËP³¦5)s¦´tñÒ5f³uÛ^½Ûæ²{Ï¡¤ÛdÈšìF?eMOwî< )Óq¦SÑõèÆÕ½3ft/ý¬]·.]ºÎAO äPçOGQ¢DòÒN*„€B@!àJ"¸»’®ê["Ü] ØÝÇ‹[}*J6î°ytƒú’,Õ&©B@! „@&°aãNzhÃåâÅkô×öýFSÜÿÛšìe|ƒ â…‹ÕññùsGQõje}lçêðl‡X® ‚[ð @‡bEóllV¯Ù¢›Y,Ožùa„¡V-뙑u! „€ð%vÍ:ÑÛ7o>ªTù’Ô£_W‚Àm]í Â{êt©¨KïNT¹FEënÈÑ~ÌŽ˜8Tw3YP"¸(n9Y`!€_æU§á#¦Y Iìd,pȆB@! „À† éIíÛ5¶àñéÓg7lÚIƒOâèíÓ§/P£¦]iÿž^"½-¶³ñÃ?P¶lmîE}Ó&59R¼ZVܳ6O:OY•ßù·(/^7N›$IcÝÖJ²¤ ½îXºpáªÑ¼F­6ƺw+H²j]R¤HLÝ¥! „@Àضñ/Ú·s?m9°Ò«Ü~)È_rþÌj^·%íß}€ –K«_HÊ1…€îN~'$ÂÝÉ@]Ø[ÓZ‚{ìØ1©TÉ‚.<£t-„€B@!T `ú<ìÍÛ9rdæWôhQ©S—ßy÷¡CÓ•+7(uêäææN[ǬLx•¿zõ†ûÜàØ7ÜC…úÁ¸®0aìÛ Ñ¿ÿþÏh«Wðýéë×õ&_,hüRùÑ7ß/ç’c„€!@¹Êe©`Ñü^.õêåk´së.ºqí&ïûðáuhÑ™¶¶«lõ¡ýþ½´zézôð1÷5oú*\¼U¨ZŽ·­äÌ›ƒ2döù!sÚ i¬•m!`Dpw1jy"çbÀþè>eʤT¨P.Ú»÷(÷R§vBD‘! „€B@!à[U£ž½†¢ÞQ.»PpGÿ‘#{ î÷xˆ¨7[7ö¿}ûÎÜÌ_ë)SzZçܸqÇÛ¾®ß¸íe?ÄuôqþüÞ·bÙd?ÛñøU¨÷2(©B@&§@njÞ¶™]]Zu§…³óþ3'ϪâUˇÓú`ïúêѯ•È]†®)!eܰ v÷ŠÕ*PëN?ëne)%Üü¶XG¸;¹{éÎÉš7«mî êUqrïÒB@! „@H!%Jd‚¥ ’ˆ¢Ü½ûÐe—s¼tI–4‘^¥˜1£Ó­[÷xûá#Ë|EºÑµë^…o½Ï·Ë”)’‡ìÙ{„ž={A1bxM wôè)²'ȧQ3´à~øÈI»‚ûâ%k•˜ó‰Ïרa5oìƒ F+‡RœÎÛ¼¢1bPºté(MˈÎOŸ>Ñ¢E‹¼ƒÀ°Ä‰S¦L™”ï~‹ýÞG7L–,+VLozYâ{ñž={ÔçôÅŽ›J—.í¥Mp­X³f =þœJ”(¡~'$QÿoÑöíÛ)zôèTµjÕàzÙr]!”@¥ ÁI±ÞDI“{>ˆu ’`·êЂº·û…q?yF=ÀþDaÆu´ i'Ü]üvH„»‹û³ûêÕÊRûŽ¿qUL–"„€B@! üJ zô¨Æ¡?3ÖµòåËN˜:pðD‹.såÌbl'M’Nœ8ÃÛ;w$ØÏ˜ËöûÙîÆ\çŸõ,YÒÑO?e “'Ïч©oÿ14eÒ@‹.!’ÿÒ{„Ey£aƒª´fíV®4dU®T’ ¾˜ËÔi‹¨mû~\•7o6rS3!­,Y²„&Mšäíe—/_ž–/_N"xð{ûö-5oÞÜÛcÊ–-K ,Pkbr;GÎS£F o÷zõêѲe˸¿È‘#+û#¯žûÞ*ïüí·ßÈÝÝV¯^Í‚ûßÿÍïAæÌ™EpÂï« Ý6óÌ¢8ñâøIl×='WɵuÁC»÷ú«?Ý—,…À· ‚»“©K„»“º¸;ønÖ«[™âÇ‹íâ3I÷B@! „€Á@Tå®Ë“§Ïõª¯–ïÞ½§‚EjÙ<æÜ¹Ëôòåk‹}­ZÖ'ˆÞºäÏ—V­ÞÌ›Óg,¥/_þ¡¦k(6<íÞs˜zý:R7uÊF¿ÿÖ™*Vöu§M_LHhÚ¬iMBäú±ãî4nÂ\:pà¸Ýóá¡@‘Âyx|—.]§ÜùªrŸÙ³e¢Û·ïÓ_;öÑè1³Œã»vö^@6Ó•„ RÆ «ûüù³â{€Ž9B7n¤^½zÑ„ Œýz¥N:„Èt|o½ÿ>­\¹’6oÞLÕªUS3÷ꦼŒ+–ÝÈô\¹rY´5o<~üØÛ‡ â%êÞÜVÖ…€ÚÖþñ§q%ÊØŸõb4òfå–•íX 5cKŠªDpwñ;§î?¥r-ÜêPÔ¨ž_ŽùpexB@! „€”@´hQŒ‘íÛï‘'ȨppSò½§u7ºë׫LcG÷ÑU¼„?fÜleió@‰í_húŒ%üÒ Î'Iœ€þ\¿]Wù{Y¾\1êÖµ=“ûZþÇÂË\Ò§OEˆ¾ß´y—¹ÚXŸ>u0U©Þ’.\¸Ê¯šµÛûÌ+ƒ~ïJÕª–1W…¸uØ”@ȶ.°xÙµk¿¬÷aÛÍÍmNÌûj×®MˆŠß·oŸš¥pRÍVøÉØ:ujZ¸p¡±íèʶJˆ˜‡ø/E KàÖ[thßa/pþÌZ»âO:¼ÿï+R¢0õfù÷ÈËAÞTÀû}Á¬EF‹¸ñãR$•«ÄVq?éN«–­±µË¨+^º(EUÉÌ¥oE@w'“wV„ûq ²qÓ.úŸú'EWÕT×&«³×hp½Fg^nBfÏžMOžÖOõþ¼j]­îjÓÀó$N=¬Å‘ÇßBy´}z‹:Üd̘Q…m´ „´Ù³g)RD’$I¢âÄÛëçhŒß0È'Ož\>|¨<×ÿøãY¼x± 9é™3gdÀ€Ãü¦M›”—<Þ:G {xžÃcü›o¾QäöæìÞ½»2¶ÃÉbá¡ûøñcÉ“'šz0¯¯¯òž§sçÎ)/xè2dˆôîÝ[…ÞqÔà~ïÞ=elÇ›Ø[âÄ~ùÄ`„_±b…Œ5Šw{‹u…@«Í¥zÝj6s¿|ñR6oØ*k—¯Ä^ªýíðu­oeÝÎU÷ Jãæ eðèê ÿtTò© EŠö¯YÕ'O‘,Àv6’€» Ðàîb® )£/«O¯¶Úɽí/9z;¯$V ô8VÜÃêú?溳™SŒ›ø1—À¹Ã1ý;w(ƒ{8Þ"·F$@$@aš ìC¢ þÕÿ…žßöÂ’À¨lO®áMàmm„3Dˆk‰5ªu•Ûî¿ÿþ{elLj _¡B™={¶òLGÝÔ©S•‡>âÓ#$ 6x+†y„nÇ;<×­|î!“'O6ÂaÀ£ý§Ÿ~RÉd^ÏùÍ›7+ã¸^‡zú!0¢;*0êc8ô˜0a‚òø‡w=Øþõ×_ªÍQ]ìGî&à‘<™äÈù!”y¾BÅ JïÁ=¤\¡JrâèIA8§±Ã'ȼåöCÐä-˜G>Ïa²ÿþ¤I—Z2eÍ$™?Ï(ñÄ7Oa·œ·`^ù†ðvÙ°2ô ÁÝÍÏ"¨în^Õ“ „AyóæU^俥#4ÌðáÕgw™2e”—¶u}º¼yóFvìØ¡¼ÁÍþùg§’{ÂfŽ«nÖåÊr@CæÀöô©_H$‹d³ºàТK—.2þ|› s²ZôG\wgåàÁƒjM^^^rçÎ5ñó‘ÀU o}àììO!AàÖÛ2N‹ÙþËì…Æt9ó„¾{ŒÅ±@!H€!e\ ÛÚàNw¦:     ˆÀ`°E{öl™6mšœ={V`¸F¨G“6",Íõë×åîÝ»‚xî©S§¶yƒ a„ʛ]ïuý-xÔ#é)ê H°jØ;ÂÑàà:á…ñðœ§@X 7^\I•&¥¤òJ)EK‘z_×ñ÷m’°°®‘\M€wWµÒÇ2V@xK$@$@$@$@$@$a $J”HðqµÀÐþÅ_Y­‡‡‡à î:>fÁ¿`Á‚æ*‡Ë‰'|($š\{|ÑeËq•.WéqÙÆ¨ˆ!À¤©r¶ÙYïgõ³? @è$@w7?—O>qóTO$@$@$@$@$ðQ ܼyG¾kÓWóTM ýú´—-[÷HüøqŧjÙ×uãÆ‡û¨ÈN㊕¾òøñS)U²°¤J•\ •áÜ\¿.^+/_¾’ å‹k^¿®ñÀuv v¶hÕÝ»÷e×7óåýBíÝ¿AÛ¶ï“ÈZ(Â…óø×…õ$@$@$@$à4z¸;,àôp˜[I€H€H€H€H ,øqìLiѪ§ìÖŒ¸þÉœ¹Ëdý†íÊÈ~ôØiù¶Eé?pœÝzgúƒ,`~¬s@œ«k÷ajüù Wœ1ðnή!p–=Ž?+iÒ•ºõÛIzmeùŠ–¬î¦LýEæÎ[nUË[   z¸_ £Ã=PDì@$@$@$@$@¡–Àºõ۞йrf“"EòÚ¬7³ç.Qõß4©)¯_¿‘úõªJÊ”Élú†dE¥Š%${¶L’Â3à˜Ô!¹&wÏõÓŒ…òîÝ{É™óséÚ¹…|ž5ƒÝ)K—m(¥K–'OžÉŸþ)ƒOÃGNʪÓíög% 8C€wgh9ÐWû}›B$@$@$@$@$AÀP)yóæ,YÒ«]Ï›;æ£ï~È Î} !½€k×n©)¿n\CjÕ¬hwúwïÞI-„ÌœŸ—Êùó~Þû7oý.5«W¿ÿþ[>ýôS»ãXI$@$@$@Ž`HGI9ØÏ:¤ =ÜÇn$@$@$@$@$àðZî7àGù"WI”4—¤ð*(•¼›ªXéfõïß¿—¡Ã&Kî|Þª_ÊԅĻ귲k—_è\«ø|+'NžSÃ&O§î÷í;bV#³fûy·7ýº¶ªß¿ÿ˜ê×¾ã£B»@×<„¨IŸ©¸+á×_ïtæÌE5bÜ’+oeiݶ¯<|øXoVW]ÏÉÿÖ¤7.úuÒݺ`~̉õ$.\•ê5[ ö€4lÜItõ½q;vî—šµ[‹WÚÂj˜û2‹¾ÎàîWßÃÅ‹We€"Ï k¬\¥™,YºÞ˜Ï{=|䔪›>s‘ºÇ8k‰%ŠôíÓN¾mZGbÇŽ%‘#GVÞð=ºGc»5,Þ“ ‰=܃„ÍñA4¸;Ί=I€H€H€H€H 8^½z-%ËÔÄòŽ)’äÈ‘Y ò—Н¾Ñw§¬Y5CÊ—ûJMQ­F+U'N,ɦ…^¹r冬]·U6lÜ!kWÏ”7oÞÊ¡Ã'åùó—ªÿõëwäþýGòøÉSc‰OŸ>Wq£E‹*µkùyTß½w_֬ݪtê‘D^ðýŒ•Ÿ¦/TÕ±bÅЛ5ƒõC)\¬¦2ø¦M›JN:/ÇŽÑÖ³Mvl](©S§P}u=íÚ61Æ¢£9æ„Y—;(=M4ooÿä°¶¿"_ÕRaX°‡Ï?Ï(ëÖo“­Ûöªý[›0q®tè4P­3[¶ŒÚÀAÝÊU›eÃÚÙ†‡¿¾ÎàîWßÃýäС“J”(‘ձΓ§ZË ß«gbïY½|ùÚz êþòåëÒ­Çp™=s„Aˆ„ÕÑåÔ© ª¸bÕ&Ùà¸^­®ïßÿ©®—5/xó>\µß²eŠẊ›’% ª·.]ºnÓÆ    ø˜èáîfúôpw3`ª'    ÿœ8qNKæYDê5h¯ÅV÷•¨Q£HÃú>Òùûom ÒUöì\"mZ7R±Þa¸E”ReH-ÔJ`¢{·­ÅHwôw~·í‰——§]1cFWÝõ8òöÆ¢î_í?gE7¦Çˆá7‡y<¸™ß@ÛsÍ𩇟ŒÌŸüù¾T爇oWí)Ö3¦_ üÀØXã= ¸›=Ü]L˜î.Ju$@$@$@$@$à ~~Ô’`>‘ï;5“‘Ã{£¦ý´À(£€-Ož<“téRÉø±ýU ŽŸ0Gú+“µð)}{·µ û¢:j_Þ¿/óY©³6nT]¯ôŠD®öäÒ¥kJgäÈ‘æÇŸŠGOšú¿ÿùýù†DŸfÁÚ•´ÿ±¯_¿-¯_¿1BÊ@ôݼù»…ÊôZÜs„ ù¦IMù®UC‹¶wïÞiá]þ$45Kp÷«ë:sæ’2èë÷¸ž=wIÝêlÌm,“ ÀÇ$`ÿ7¾¹¢0>·µÁÝQo—0¾m.ŸH€‚M`ÏÖ-²nébõ¹÷n°õQ @Ä#páâUµix\›eó–=æ[9¨ŽS¦.¤¼áïܹ§Ú ´{·–¢µß¾}§êu£±îŽÊÕk¶ âeJ‘äÉ“ª~Áù‚¥cÇͶPñðÉÊ‹|Ròä¯"Õ|Êjo<••ZLwíñVu"Xc°UÁÑýêÃðÖBî|ÞR·Ž·¼{÷^ýºFíùóÏ3Jõjåônv¯K—mP±Þ‘صgÖvû°’H€H€H€\I€wWÒÔtÑÃÝÅ@©ŽH€H€H€H€$0fTo¹yëwÙµë  >Eúòˬ²fÕ iúmw¹ÿ¡2Ãp»aíliݶŸÀ[|ÄÈiªïgŸ}&µjV”É3vêÐT3€Q‰WgÍ^,yrgßM»$A‚xâ]¹”Ñ/8…"…óHnMïÐaS´Øó•*ÄC;¦¯Ô¬QÁPݶMceÇ¡Á’¥ëUH›&Z ùÚ5+I¯>£Œ~Ž ûÓO'«Š+}„…™3k”æÉ¾@öíó;‡>¼¹»uÓ|ißq  §3xÈD5 Ðè×Qz÷jãè´*9­#ûÕŽÞC–-ߨ’ÜêuHl»hÁxÅ@¯ã•H€H€H€BÜÝü´ßK)$@$@$@$@$@!@ aÂø²cë"A\ò»wïKš4)$I’DjæKç·É©S´:¿œ2¤‘;ó”ýalGÿ8qb[¬^ÔçÏlUqÎÿüóOyñâ•,]œP<’&sÌu}OÕ²òÏŸ~¡môºk—wëE‹«ußï;6Ä++† ”ŒuxJ$3ýeÞXÍßGyçc…éÑý; Ý'Ž®·¸·žKo¬æSNóV/'7nÜQ^ãY³¦W,6ðÑ»Wx¦Ïš1B¦N,çÎ]ьݟHúô©UrZ£“VpÕ~u‰eëæ_´¸òwäöí{оo½®[´kY»z¦uïI€H€H€HÀ­hpw1^kw«§:     @xyy >f±¸`Á\æ*UFGB¡DMëMä===lô¸¢kÌŸß2þ¼=½‰%|\)©R%|4äȑّ®öqt¿º„ùÑCýèu¼’ @h#@ƒ»›ŸˆµWŠ›§£z WG÷èoûäþ½»’6SfIŸ9‹‘ÌΑ¾}óFΟ:)·o\Ïâ•.½$HäçéØøàŒ L7ÛI€H€H€H€H€H€H€Â'Ü]ü\ÿý×Å ©ŽH€"(U‹È®ßËÃû÷ Q¢F•¯Ê–—¡S~’¸ñãõÖ…“GKï6­äì‰ãòÏ?ÿÍ8-[ÅGÚöì-™³ç0êÍ…àŒ5ëa™H€H€H xêÔª¤â½§Ií(xÚ8šH€H€H€B†@¤™&âÎB÷ˆûì¹s  X:wŽtlÒÐÂØmïÞ¾ßU+Ä»`9ú”Ý æNž(5¿*,§µ0¶£3Â~m\¹\ª-(Çì·œ±6ÊXA$@$@$,ˆK?qü—„¯ ÖB8˜H€H€H€œ @ƒ»°éÊîŽPb ˜ŒâÑ¢G—~£ÇÊúÃÇeËɳ2`ì‰Û/1Üíë×¥K³¯•ݬ Þé:µ$µƒøÔo(«ö”]ç¯È‰S$Gî<ª†û¦>•åå‹ê_‚3ÖP @„&@ƒ»‹¿µÁî.Lu$@‚þí\¸y»4nÝV2}žMÒdÈ( [~'‹¶îT†x@8sü˜òv7Þ«»qÛø»¶2zæÉ–3—xzyIÝfÍå§¥+%j4$½yúø±ìÙºÙ蜱†H€H€H€H€H€H€H€"4ÜÝüøipw3`ª'—ÊW«!Ùså¶Ù[ælÙ¥V“¦FýâÙ³ŒòÕ‹ä·ÛÕ}ÌX±¤SÿF›^H”4©ô6RÊxWUŸçOŸª¦àŒÕuóJ$@$@$@$@$@$@$@Lšêâïkw«§: ˆJ”¯àï>½kב¹“'¨öë—/ý®\8o”ÓgÉj„Ÿ1*ÿ+4hÑJð1KpÆšõ°L$@$@$@$@$@$@$± ÐÃÝÍÏŸînLõ$@á’@ò”©üÝWÊ4i¶Û7®Ëßÿ­î¯]ºhÔ{¥Ko”)g¬#úÙ‡H€H€H€H€H€H€H b ÁÝÅÏ™î.Ju$@$`EàÿûðrŒíºÁýí›7FO„”qF‚3Ö™yØ—H€H€H€H€H€H€H |ø`µßûüh»£‡ûGCωI€Â0›×®J¾¢ÅìîàÎÍF½‡§§DŽYÝ#±ª.¿ß¼©m®wnÜè‡$öð´3©¤¬zGgÇêãx% ðŸÀƒ‡$ÒgiüïÀ    'èáîâIw¥: IÀwå ÷½aÅr£-UÚtF†s]öïÚ!/_¼Ðo-®íÕ“úåJ©Ï®Í¾ª-8c-”ó†H€HÀ‚€———xj‡£ [Eе­d @˜'@w?Bkƒû'Ÿ¸xª# @`Û†u²{ó&)RºŒÅn¯_¾,¿ü4Ũó©×À(gü<›dÒ>çOŸ’W/_Ê”äˠ!F; ÷ì–cöu¥*z«rpÆÊX  °!=zt¹víš|¸½&Ö‘ B€!eäl³µ‡»³ãÙŸH€H€H€H€‚J f̘R£F ÷ññ‘8qâi,…>Mš4 ò¢‚36È“r „4¸»ùAÒÃÝÍ€©žH€H€H€HÀ‚@P¥Ag19oB ºuëJ”(Qœ^OÞ¼y%sæÌNã    ?4¸»ø;î.Ju$@$@$@$@N(V¬˜xyy95ÆÓÓSJ–,éÔvÝâÅ‹'ÞÞÞN/’/N#ã   ° @ƒ»×ß|ò‰ëuR# øGoX6nÜØ¿f»õ5’H‘ø§]8a¸ÒYã9<âëÔ©†wÌ¥“ ÀÇ'À¤©.~ÍÃ}£ïNyþü¥¢-Z©\©”‹‰†>uëÖoÓ‘½Q +U²Ä7T-ò?ÈÎ]ÕšâÆ-eJ UëãbH€H€H€ÜO÷Š£¿›:k wÿ8ƒ+”-[V<<<äîÝ»©«R¥ŠÀ3žB$@$@$@$t4¸Ý‘ÿþkYžc¸ß¹sO*y7•þùÇØôÙS›%S¦´Æ½½ ·oßSM ÄÓ^yö´éæH›A!TѦ]?¹q㎚mÿÞå’7ï!4³cÓœ>>'NÛÙ>ÀËýСCnÆQOø•°‘H€H€H€H@èáîâokw«5êÌ¡cêÕñ–ºµ½µÍœxX£³‹ ¯_¿‘ŽËªÕ›eá¢ÕZ¼ÒòàÁ#‡µ¿yóVÿuñZÙ·ïˆScõI®^½)K–®—½{Ë“'Ïôê@¯ˆ¿b¥¯9rJþþûï@ûëîÝ{ HZ»xÉ:56¢|ïéûç5d èÔA¾­^UŽØo3áųgäÛjU$·g©œ?·ômßF?|hÓøÞþiÌH)•=‹äOí)Í|¼eý2Ë·d0æšðà 9wê¤Ò] M »ºûwl'CºuVs½zñB7øC(4,™;[µ×nòºÚû’ûÓçß¶a´ª]CŠjù. §÷’oªT’]›|õfãúüÙ3ù¡[©^¬b\!Ï—2ªooyòÈñÛ e, DXQk°àÂÙÆëÖ­+Q¢DñwWyóæ•Ì™3ûÛÎ    Ç ÐÃÝqVAê=Ü_¼x)K—mP<"EŠ$µkU’O>éøý Ó}ÃÆr÷î}ñðHlÁ,!9xð„EÝü_V >ñcûÉü+íÓ¦ucC’«öé7F~]¼N°.³à•ØJKȈaÝ%}úÔæ&£|øðIiÙº·?~Ö"=ž›OÕ²Ò§W[É‘#à?>–-ß :‘[·~7ôFŽY:vøFúõi/Q£ÚþqãøÐa“eÜ„9Æý1¢K±¢ùdÚ”!’­O’ïûT]ïß»+[×­‘«Îˬ ã´W>ÿt_8sZ®hý þù§œŸ2b¨M¨—k—/Iá’¥Ô½pÓ6™¶d…š'a’$ràú;w¾1ïê_Êûwï¤|µ+vl£ÞÑ‚;ö‡7Æ (ÿûßÿdïVY{àˆì»rSúŒ£–õC÷.òöß¿­Ã{÷PÆöR•¼e÷Åk²jßA9tëž:ȸvé¢Ì™4ÁÑ­° @'€ÃËÆ-Mu$58OP"ÿŒêð|¯S§NÄ€À]’ @àoØn†=ÜÍÉRëÕýJ¦nÊMs½rÔðž²zåtiÒ¸†^%_˯êP_©bIq¤>¸oÿ1rþüu› A<åÝ}øÀj¹wç lÙ4_ræü\µÁ0?mú}˜ºÂ˜Ý®Ã勊† |äÐþUrõÒ.™:yˆ–X*‡ê÷öí;©T¥©]ƒ>:LûidÍšAÖ­™%×.ï–? “ܹ³«±ø2vÜ,¹sçžqB—nCÅwÓ.U‡±+—ÿ$7®î‘=;—H½ºUT=ÞÀaÂÇÕ=¾À‹·eë^‚ð7xñâÈ„qýåÔñrú„¯zC zôh*¬êÀ/$àfÍ;u–Ï>ûLÍ7~|8~’*ÃØýúÕ+‹Ù ~UBræ/`ÔUkÐHŠ—« »Ïñ3¾ZÁÝæqzY'S«ñ×z•ÓWgÖàÈþLŸ¦óª7j"ù‹}¥ÖƒŸZµ‘V]ºK£–­å‘v°<ë–ú…æ¤1=ºê Æ?Lš&ÍÚw’$Nï‡H€".ÜíýNêŸ!>â’ ß;/[¶¬öªíÏ*Uªh¿WÆ ß›çîH€H€H€H  0¤Œ‹a‡w÷sšg*’£B¢D‰,Õ|Ê«ù”“ïÚôUÞÝ—.]S^Ø… ç1ÚõòùóW:OϤÊЮWxyyªb@}ô¾ë7ìЋ2h@'iÙ¢¾q_"qB™=c„òGåNÍ3Ü,Ýz 7nÛ¶i,ã~ìgÜ7ÿ¶®xW.%i3SÆíÇŸÊæ-{´½–3úè…)’Éo{– Ýo¾®¥BѤL]Hy«ãûáô™‹Fx˜cÇÎÈì9~¯òfÒâ7Ãȯ‡œ®‚sI̘Ñå§é Õø5ƒýA~qª§LýÅ8`ˆ3†lß²@²gÿî&K–ôR°@.åÿÏ?ÿèKä•ÜBaN>ÿ2§…nx¸ÇŒK^j1Õo\½"™³}8|*RºŒE_Ü,QB¶o\/×5v³8«Û<eÄ?uôˆ¤J›Vò)jÝìн³kpdzœlVÜþªË !ƺïÛ«ÊðÜO’,™QB¡%ÕÇ¢’7$@$ÔZ˜¯¢E‹j9nv= ,(2d0Ãú@IDATîYÿðó¦Aƒ2räH‹Íúçùnщ7$@$@$@$@ ‡»Ã¨ëhmpÿD>ql`éeN–Z¡|q‰'¶±ò¸qcKùrÅŒû™³Ý—<ÞÞ³4ƒúÆõsÕçÛf¶¯Á¾xñÁÃöéÓçÆº.\¸*Ûwü¦îcÅŠ©ŒõFã„É5¢§T­RF}ÌãÍ};¶ÿÆ0¶ëõð<7Ç}¿ví–Þ$ã'ÎQ®¨À!nl7:hØu/´I“çÉË—~ûصûáÁ×MjXÛõñðê7¿u ×óJ®&à™ÊËø>5ëŽþ_õ—Ï?ü?‡ö”iÒ˜»©rŒ~1×_¾°ìë¬nkÅKþ󘯡y’ëÿ/Y÷ ìÞÙ58²?Ä•‡ÄM ÀéŸ?óë/~ÀýTÂF °"`mTµ¾·êÎÛpJÀú¹Ãã½LÛCñpº}n‹H€H€H€B„ înÆTc›—$õýõ—Ì›ï—à ìvëÖñ6t/YºÞßP,F§ Æ¢Lé"Æ;µrêÔyAÓ>ýFKÕê-ìj?Á/ ³fM/±cDzۯU˲|éTõçº=Éž=“½jñHú!a,bÚërêô½¨ {÷ëÂä *äùóZ’³Ûªl^wíš•T½/åÊ~8ô°×Î:pÄYG®³<}üXÜó ¡”Â+µ¹I.iñË­åÒ¹sªÊº¯³ºÍz±¦• 竘ÄÕ4679Uvv ŽìÏ+]zµ†«?ü;€ üÛÚ£Uséôu#¹yõªèýîܼ!ïÞú…Ò¿eíjÕoúØÑz¯$@$à5jHŒ1TßhZBéZµìÿnã2v ³²dÉ¢…NüðjÆ Uô0»!.œH€H€H€B!†”qñC±öpw±úªnÝúírÿþCc Óg,²IŽŠ˜çº ±*¬6kjë}®÷ îuÇÎý2Kó¤ß£®oܸcx¤÷¢–€P—ôé¼ôb®1cøÅV¶lï ßÉ£KZßéůH² £ý¥K×~©R%7ÊÖ…Ô^)¬«xO.'ðâÙ3™=a¬´ø¾«¡{òð¡êÿÁô™³Ø„B™;y¢Ôhôµ T äÎÍ›²ü—ŸU¹H©ÒêªqV·>W¤aø/V¦œ$Mîÿÿ'æ1öÊήÁ‘ýÕÂê¬Y¼H~=S¶øNâü/W¯Cr×á?ÍT‡ð˜‡ñ}þOS¤i»Žj‰5´{W-_Ä%É”-›½e‡ªº+W®h‡ §$räÈR¾|ù ¿m”Maî›Ú÷Øëׯ%]ºt’1cÆ ¨±³k×.y¬}oéòÅ_ˆ———~j®gÏž•‹/ªõ„Ö5†Xl!1µ7`tŸ;w®øøøho)Ɖ`¸]¼Ü:¤n­=Þõ>¼’  îAgçÐH-^¸¶Í²iónó­Ý2’§ºËà>`à80hœ1/BÚÀã¼W9¡ýÁžE3<ýí7Ùá»AÍоw_ã* ØÞóˆw­ºòþÝ;Y³d‘<{òD2fý\ÊùT·XY”¨QÒm¬‡“©ÙäksµÓewìϧ~C™7u²œQ"ûñ¯šbÆ;¬œIÀIy ‘lZ’Ô)#†ÊÆ•ËÕèXš·dßQ?*C²µºã&ÊO£GÊÔQæ‚ÅKÈøy -þ¿Ecj-ôJ£V­eh®éÖÞÕŒb»6ûJ<-Fz©JÂ[éíÎ\ݱ?ü0ãЩ½¬Zø‹LÖØAbh^§úö7<ÙQ‡õÏY½^z¶i©¼âQÉS¸ˆ ™8ÅðŽ÷« _W¬X!C‡•¾}û:ôö‘+vcû±cǤwïÞR²dIy«…äùý÷ߥP¡BÁR[{ûà™vht÷î]Á¡&Â1„ÖŸ÷{÷îUâ¬Y³†Ú5ëa¸hðÖ­[¥lÙ²Z2òóÜŸj¹òçÏ/ëׯ—Ò¥-߯qÑRBTM±bŤH‘"êÿ˜“…*ñ´7¬¼½½¥xñâ¡j]\ „4¸»ùIÚ -âæ)Ý¢þçyË Ï¾äɓʫ{lŒdúÄHhêá™O{õÞ/ñ’§"©+!dtiÒ¸†Ô¬QA¿5®µP,ö$SƆs„¤yñâ¥]O÷º Úi^’Ç”ŠG÷ ¶ÁŠ2fHcÍ÷8æ¯Áý—+5C‘_xžF «)¯×tšq^7Ö/[±QòæýÂÞödá"?oY»¬$”­â#Wß~È9€®ë·aݧY‡N*>{ Íã¡d¬ÿÍ3÷©×@®^º(< Y²Jì¸q-t›oê4ýV|4’ §OIT-Ö°=Ýy ¶YïäEK$qRÆÄ¬e뵣μ>{}\½?xΜ>K†Lš*—ϕϴp+)Ó¤•(Q¢`z )Z¦¬ìÑB`Ý׌¼ˆçî©ÅÅO¤yã†ùßÿþ§½idÿ`Ð{xõê•8p@*V¬(ƒ rùˆ}ð4ž„VÁ[©S§ÕküØìpxR¿~})Uª”ñLý[ÂÁ@Ý´iS¹|ù²Ý[üëñoôÂ…¶¡q­\“{ à-œ9sºwj'   J€w?x-jH¸”Ùs—ûªS»R€ÈãýZ5+ÊÔi¿¨1óæk^ŽCº¡E—çÏ_êE‹k`}nÝþà™Ž„©ÖÏÃù¿¬´®V÷Ù²e”lÙ2©«/_¾’¡Ã§Èƒ»Xôݽû alGƒwåRíA½iØÀGV¬ôUÃÿ0Qªx—–˜1ý˜é:§L/­ÛöU· 䔦ßÔVe$n]½f‹*Oûi¡4Õî3h|³lظC6úî4W±Ln%ò—ùò;ž={ʾ}ûÔ¼_}õ•:D©T©’E\ýùóçk‰ÎooÄ…â·oß^…úÑ7‰ð8´ÀüÑ´7cpp“>}z½Y]aü?~¼vØS%óµhôçIVßkðtÇ™#Ò£G9zô¨#]Ù‡H ˜Ö¬YCƒ{0r8 @Ø%píÊu>`dØÝ@Yùêek¥N£Z;Nì²ãˆ½MÜ#öówh÷H|ª âHö˜,˜KK—L‘ÑwÖì%†Á½Há<êÕ÷ïßËë×o¤}ÇJÝø±ýŒXïõ©Z¥´ ;ŒÖÐÓ£×õ1¯+qâ„Ê0Ð,˜'m†b²ð—ñR»V%)þUéß·ƒŠC’i?-Póx”c}ÆOC­«ƒu?mÊ©Z½…Šmøö5k·¶«oðÀïµC?C”ÞaÖôáR¥ZsuxðèÑiù]/½É¸véÜ\FŽúɸg $mØò;I¢y$S†  ’ô!‰'’TÒ’ívïÞ]*T¨ ðˆ†4ŒÍ ƒvxºÃKÆõ‚ *ïpxÃÈŠ˜ì0Ðêã,Y²¨±˜ÇÚËñc$„±ºjÕªZ®DjE‹UËÅþpð0yòdå™>kÖ,}êZ§NåÅØâ î.]º(/éåË— "t¹yó¦@/B÷ y+ŒÍ/^¼³¿þúkk^ï”kPŸbÈÃèõÀóñóáycbßoܸQ½Å ;$…Õ† ”a-_¾|Ò¿u8ƒ ¼ÀÓ z¯]»fs@H䋃 °6¿a#¼¹‡g²dÉñõõ•Ê•+ÔÝhCHË–-µß'>„³3:°@$løwoàÀêß`+£ £Þ¿{¯V#Fté;ôƒ#JÝN¸\v·v=Õ¾Þ¿ÿ3\%@ƒ»-“`Õ„7wxNÿºx­Á^àŽþ®§y¹Ã{‚P'ð2G²UÄ€_0¬tí>L pÓP§ÿñŠÀúÄŽKÖ¯™-í: uë·:P€çY»¶¥wÏ62~Â\éÛÿƒ§£¹cß>í4Ã{~ùN ßræÌEs“2`´mÓXúôj«yµÅ²h îMúô©åÈÁ5Ò­Ç0™=g© ¬YräÈ,}{·3(ÌmŸžQmѪ§zkàÍ¿?æÑ?\Ý[Ê•-Fƒ»Ëa†@æì9dÀX¾¥f˜ …qsÁ‚ÆaˆEbOxë^Ý €Ÿ0"#üÈ—_~iŒ)S¦Œò2†·7 Ÿ‰?¾øøø(ƒ;îgÝl˜F]HK§N”ñgñâÅ£°.s2iÒ$åõ>{ölµws{Ÿ>}TWæ¯\¹¢¼´qHÆ'NXì s¼|ùRðö€9)l®\¹”÷öàÁƒõiƒ| ÊóÀd…±HHŠð1?ÿü³áŽ·ÌV;wVFömÛ¶ <ú!%T¸pauø‚ï!ÌoOÖ‚ƒgÞúø¾Ã£w]?–°6 €ë <|øPÜ]¯™I€H ìˆ-ª|ÓªIØ[xX±np[åÿ#@ƒ»›¿ÌFd7Oåõ0â¾xz:HºÝ:6º®žÛøÀû!gÇ<^¼8z³ºÖa^֬𡽖~Gο¬¼Ø‘”4}z/ãûÞ½ÚHõjåäø‰³â©úó峌™\D ÇrêøF•8õĉsrï’6M*¥Ã:¶º¾¸k—wëE¯‹Mô· Q£F‘q?ö“±cúªC‡“§ÎkF-1d:/͈2À±qãÆ–_NTIl‘Dõâ¥k‚D°xû@70üóçÕu°‘H€B’@»víl¦K‘"…T«VMy­ß¿_'þn ÞÑfc»>ÞÞ0¸Ã+^7¸ëm¡éŠä­ð¬7ÓÍëƒúúõëU’W½bÑ#ÌÌĉ•A^ïP&x»›±Þá…n6¶ëíµâ ƒ»®/¨Ïá|pТÿlÒõ™¯Î²ÂaŒÞ0ºëÆv]_„ ák`ˆ÷OpAH#g¹ /ZÐ;£ƒ}I€H€H€H€H€"Ü]üœ­=Ü]¬>Ü©ƒá9E ¿?býÛ\`}R¥J.øø'0Dë±áýë+VLÍû,Ín«Ç ÂÖà㬠’¿âC! ÐL1Ìí‰^fƒ»^o=F7z".zh$ÿ|úô©2†û·F„X <Ž.8L@ìyÄ Çâ#ä â³Ã@o–gÏž©0;ÅcOàõ0(®’ >ÄáÈØVÓ‚ßµü‹£î_½Î¡f ú÷’^ØUg€d¶     ˆ îÑqA[X÷pwª  ˆàÜÓËˈÂ{À ›1cF‹6ô·'zJëD˜öú~¬:$ëDÂSć׺=ƒ³ÖäóÏ?WËüûï¿UvÄŸGŒq³ µÁ=Nœ8*”<½í êÿüÓuñ!Ýõ<‚ ÉgaüFLxÄoGø!]àýŽñI¬X~aâgÞyýúµêníUïŒö%    ˆì¸Œ{wË.éáî¬TJ$@$† ôèÑC=zd±ƒÕ«W«°*åÊ•S1ÇÍ0H#¼ŠY?~,;vTUUªT17…º2Bå aèÈ‘#mÖC† QñèK”(¡Ú‘ô]{‡ôø%·VTºtiÞdÂ˼0$#.º+ÅÏÃYVØîbMˆÕÃì‡ÞÞÞZ˜¹n]?ÜÁóqFîܹ£º3ù©3ÔØ—H€H€H€H€"&nAsÿnßµ½?žÝ>)'   LàÈ‘#²k×.µ 7‘ µ^½z‚p*hCJÄÜž6mšÍJS¦L)ݺu“•+WJTüòU«VÉü¡Â­ ¥.ˆÉ6„À ~Ô¨QªŒŸÁ0jÛ‹¯:8øs æ:<Ö!0ð"Ô˸qãÔ=¼Ø‘HS÷â8p ò´†ažîµjÕxfÃ(ŒØê%3þ|#¬ B¯ Ù)¼Û›4i"E‹•C‡Ɇ  ‘„IS‘|IHq?zôh5ÂÏ Á,˜Àp¿hÑ"AL|ÆÀã‡ÁGŸBéüúë¯êY`}¦3ÂüY²d˜ÅYV‹D².\)S¦¨g¯ëCÌx$'Eˆ"ÿÉU!W¯:—ëD7äÓàîYÖ“ èhp×I¸èJw¤  0M`üøñÊø £7ŒÀ0¬Ož’tšÆÿöÛo‚wÄLoÙ²¥ºÇþÐït$A7ožªÇ…ÁÃ:þ:’‹bosçÎU胡ìmÛ¶U!Tp˜;wne¸FÒOå5j¤ŒÎX#«”)SFðö@¦L™dÏž=6Á5¸;ú<Ž;¦DÞ¿oì·C‡F Ö÷ °ïI“&©P;8ÔÁ[Ù³g¼ùаaC¹}û¶1§u!O¿|-8ˆÀ›Ž \ `A!     €Ðà ´™ÿÇpÍÎ@!  G@7ë/[¶¬Š+ïcxL›coë}ÌWÝãÛ\g¯ Ã<>î„hq6LKŒ1dĈ2lØ0A’W„ÄwuìØ±í.mðü‡çõóçÏ•QžìÐí bÙð/xx`gΜYô㮌ᎹyÅ‹W*öÖP³¬t]8|ÀÇ,ð¬‡ß?Á>J•*%Û·oWoNċϿ®F=t‚sÉ’%%GŽF= $@$@$@$@$@$`@${•¬s†”qKj" Ûൄ¢ÛÃö.-Wolì†aÿŒíúü΀%ðÈ×íz[@W„æ!Y7¶Ô74·9ÃÊÞ>NŸ>-PÞýöÚõ:$[E¢Z 9"‹/Vo78{èâˆnö!   &0{ê\ÉŸ¥°ú´hØ:Ðé—-\nôoÕØ}Ž.K~Yf̃²µœ?sA0‰éÚÖ/´Þõ«7dظÁ/¾ÿoýÁø}å’_þ›¯[6vÛ¾Ÿ=}f̃²Y>x$UKU—GÕ7¯ûåíyøð‘1îÕ«×F; $ð1 Ðàîfúôpw3`ª' Nž<©b˜ÃÀ¹oß>õÁÆŠñ¶ƒ+H(Š˜æÎH’$I$gΜΠ•}û g‘hÑ¢¹õy8³ÿú" -Âíàpo q-Âø dQ`‚´sðàAÉ—/Ÿ¿Ý÷ïß/qâÄQInCËït—/_VI{ý]ô —…prD4ð6Q¢DþNéˆëÁýãÈ!’.ëù°§¯LI“­uòÞõ„yï^?oAü¿À<®gL$@$Ò2žIÒeL'—/\VoýmZ·Ej7¬iwÏŸ=—]Ûü~‡ÆïBUjT¶ÛÏ•qâÆ‘´éÓ(U(›eåâU†±=CæôR¯qùßg~&Í„ ãbĈnÆ2 |44¸»½u w«§:  —àÅ£¸9á&~©‡Ç²+±ã›4iâ”*çÌ™ãÔ˜ÐØ‰Wõ¤ŸŽ®oÛ¶mn}Ž®# ~ .T͈WpEÎŒÇ8äA›€Þð8°¬_@:\ݶiÓ&éÝ»·Ãj'N¬ò|ûí·6¡ŠœÑfÅŠ¼Y`ûÞ=ú Ƀ;žSÓ¦MÕ0¼ñÒ÷S§N)c€ƒ‡È‘#ë[ˆ×3gÎü³fÍJƒ{„xêÜ$ €»ÀIàüé ’#WvwMá°Þ*5*Éè!cUÿu+7økp÷]»YåaBÇü…óJÒdIžÃÙŽ5ëW|ìÉå‹WŒê>CzJ¹Êeûv]Û>MhpwóÓ-ÞPnÞ&Õ“ @° À«w eÎzy»cCçìÙ³ƒ<­»žGdg ³Æv]…£FtGûézCÛÊ}ûöĸŸ:uj—÷àÁYºt©ò6ÇÈ5‡¯_û½žŽÃ//¯ˆ°mî‘H€HÀ ^káNJå/'™²f”ºš‡vzÕ$qÿß sà •Ujx÷í›v¨Ÿuöò­Y¾ÎãS«ªQ6®^¾&Ø[–l™rRÀ›­Î^”'ŸhÆü|òé§ŸšÕZ”Ÿ,¾¾¾jØ;w¤mÛ¶²{÷n»ñQ£Fö¦°¨sÕÛ+JyC$@$@a€’öë:@ö,%˕ЌﵥLÅR!ú•9¬ ~W€Ñ½bÕ ôõ8 T®^I•ñFòîí{Ë6ßíòôÉSU3VLÉ[ · 5@öÅ,FN’ £&«ªµ;Vʽ»÷¤SË.rãÚMUwáÞi§ý ž?¨û>?ô”†MëKíŠõäØáòòÅKCO™š‚ß7|jW‘áãaýGȬ)sUûÔy¥D™âF_œ]k†$YÕø²•Jˈ‰C¥w§~2oæ/{_Óï¾ÖbÞ±ÐϰG€w{T‚Qgmp§‡{0`r( €¿Þ¡bì Â^»vMG7º8pÀ_ƒ{@º îܹұcG5ÕÙ³gadcÁZðVĨQ£¬«yO$@$@$`EÞ›ÖmV$-­^×Gê4ª%9r†LÈsX™µ+ÖÛÜ·lØjüQ¤xaI˜(ÚÁÉc§¤qoäöÍ;;‚Q|›f ?T¸’ÌX8ÕÂðýæÍ[eøÆ€ý{HÍXo%‰úwoß}P†<þ¨SÚ—Z^õêúúÑçÏ?ÿRuú— ¬zöÓ¼þw²qŸÓ®“Wp„ îŽPbŸ ¸xñª?q.À± Ä•lŸg”ĉØÏÝ[¶î‘Çý²`)œG<<»{Jê'   ·H:µdÏž]%‡ÅDðNª4nÜX~øáAhÈ¡C‡ì܃ªßÙq·oß$ÎEòUÄvGLøÐ oÞ¼Qù?~,ÈMá̺01ÓoÞ¼)É“'—´iÓ:<F‹ .È•+W$UªT’9sfÅÆQ&¹…xõÈ¥ï›9rØ}ƒ! }ÁÙ{@zÙF$@Œ¼3&ÍRxŸ×iTÛí!gÌae6¯ßªbµ›Cõ­ÓŒðºT­å­Šýõ—´hØZÛqÈÞ{H©RÓ[û™E;8Ø¢y½÷$ZmPµ‰º°O’§H®«0®#QÆö<š7üW¥Šª\Ãdûæªiòœ ’!Sz‰Ÿ0¾¹«M9¸k=|à¨ÜûýžÀsß§VI—!­¤·òÜ·™”$ðÜ]ü­@÷@×­ß.ßwqìUÜK–((Æõ—øñã~PB¥½FÊ‘#§ÔlkVÍŠ%BhfNC$@$@$@î#`ŽAŸ?þ`M°np‡ÁûcbÒwîÜYÖ®]k1½§§§ ¯Sµê‡ø²”råÊý'Mš4ƽ^@âÝŒ3ÊÓ§~¯Ä#æ½½P=9sæTC4†m³\ºtIðV"౨ ˜µlÙÒH<ª×›¯ÇŽ“N:)ƒ÷?ÿüc4áMÙJ•*I—.]äóÏ?7êͬcûí7aAÖ‘ @D'pîôy‹3ðzGxW'î6‡•yöô™ìݹO3€Søñvܦõ[Tá[*ùø…›™9y¶\¾pYÕš<\j5¨a<.$ ,PºáYn-0Ü〡dÉ’*inëÖ­­»È¾}û¤fÍšbo<Þ†hÖ¬™âm3ÐTœ½›Ô¸¤~×s *!L@ÿ™·zÙZ¹réªKHÜ¿ç÷f¼ÏׯÚ,zˆ7G•X‡œ©V§ªÔÕ<ß16;:‡9¬ b¨ë÷[vižå~?׊—.&qãÅU*—ÿºR]hÞåfc»>Æ#),âÔÏŸ¹@ú ëmóÆUÚôi¤{ÿ.ÛuA½ºb­8 ±=¨O b£ÁÝÅÏßÚÃÝÅêìº(Q"ËÈá=lÖÿìÙ 9pð„¬]·UµÝ¾}Wš6ï.Ǭsû?¾6‹aEˆ˜7…šïÔ©óšG—íή\ÌÉ“'ÕëÝ®Ôù1u!…ÜM1Ççîi¨ŸH àÑ ÏfkazãÆÊH‹?î£G.ãÇxU`ø½{÷®1áöÞâ0l$0j(P  .vÛtÃ9ŒÈ­ZµR^é0¾Ïž=[Ö¯÷{í}Ò¤IR°`A)_¾¼ÒQ¯^=Ã[}Ù²ev î+VøýN‚0@ùå—²páB5¾I“&òî_Ù±cÇJ’$IÔ«ïªQû‚C-Z(ƒ9¼ÊÛµk§ŒÔqãÆUIkûõë'>”Y³f Åâ^x¶wëÖM¿•Úµk+]ñãÇ—mÛ¶ÉüùóåèÑ£*~n:uÔ቞lác``Ö½Ú‹/®¼èBÆ}ŒÅ3ÃóèÓ§äÊ•KÌo8 žñº±ëíÙ³§Šù‡!$Å…‡; êþIpöîŸÎàÔógVpèq, „/ðÔÆÇ•‚çSrÛøÀ  Ã{ Í£c‘´hÕSµÃøºný6©T±¤¿ýÙ¶ à<¶PH€H€H€‚NÆÒŠ+ª`òäÉâíí{Õ¿Îð¢Cüp³À‘ÆbxXÃ`¬ ¼Û«W¯®ßZ\ñÇuóæÍ-ê¬oæ%(wèÁ~çÌ™#Ÿ~ú©R ã=ŒÍðàþõ×_UbÍcüCõàÁƒß}‚Cx¥uAìóuë>Ðq×õ„°ú<è_´hQñòòÒ‡ª+ŒÙø½2qâD5ŸºÑ¾¤L™Ríà2cÆ eGHHÿþýÕ_À ðºÀÐC„…Ás~òä‰ìرC*W®¬ºtïÞÝ0¶ãÙB7^ù‡à`o3 ô# ðìß¾}»Á ÏóâE?£I̘1•qÞìÙŸ)S&Á8Ü0‡¹QÊþûœ½›õ°L$@$àx÷ë6P†)#&“/råpN©·9¬Ìwÿ#ZÜrxÐo\³IõŠ%Š”÷.«Ê<2¼ÞhQ ~ž1ߤÉ~ñé“g6÷´élûÙôZW¬5IÒÄ3fŒ /‚##4Ü]üøéá4 ß6«#}ú„–ì?p<@ƒ;2\8x\nݺ+%ŠÐK%µ˜<={Y.]¾.©½<µØ•U‹NÜÀchß¾£r÷Þ}Éœ)­dÉ’ÞøC&¡ªùÆ;rôØiI™"™–Ì+‹ñN@c_j¯l?~V›óDŠô‰xjñ²e˨y¦E hX˜kC"–2eŠhÞw;Õ>#Eòû£ÙÕÁˆøàôÝü³«ç )}0†øýÑûIHMÉy"2íÛÌœ4)"£àÞI (t#lPƺz Œ·0@#|IŒöÿpÄÛSEŠ tjüL…ç³zU̽zõ²ù™Ž5Áx OuüˆƒxÃX4iRe|Þ´ÉÏp/w³ÁžäÏŸû…¹Cÿ 28¼B$7Õ=ë³dÉbalוÀ@„³Ó¦M„œa!f`ü‡9^ëð.·xÓ#< ågÏž©+æEöŽ>º±]Uj_p؃8!ðlOŸ>­<äáéA8]ð€ÙØ®×#qj5dñâÅz•q ÎÞ %..ðg–‹R „Aø6üÛˆ+D“Oסk3çùêú`‡‡;BÌÄ‹OtOî ê³+³jƒJ ˜î’åJ¨¤¡(ÇOOÅ‘ÇÏÚô™ÒÉ1‡†M®ÙC¬%F,û¿‹X÷ ν+ÖŠd©*܃JÎÁqÚïº ¤OïeÜoÞ¼cŒJ®ˆÀx y÷ú‚lÐŒ´­Z÷Ö^k¾¯ê/š(5ªû%ð¸téš´ü®·Š¯ÿ`D'ü°Í“'»L4DräȬÆôeÁÂUÒ©óc=苬ÛåË}%Ó§ õ7±+~€6YÆM˜£%{dL#Ft)V4ŸL›2ÄæpîiöŽß’Õk¶h^Loq(ĉK¾nRS† ê,ÑüÉÜm1 ŒÜäÍ“CÜ[4¯/“&þƒ:(Ûê?p¬ 4^%+Ã+æa]ࡾ$I‡õ­pýa€@’$Iåü£ÿŽ…%s‰$ªÀ€yöìY·® ¡bÌÞÙæÉ``EHxEãc„˜ALn=Tй¯£ex“ÃhoMb=žpð(HàùÁ8x^Û§ .¬B± É@a@‡À ¬Ü—/_®ŒóºÑ÷ºÀ»ÝAè]àUnxToC¸]ð\ 0¸ë‚=ÅŽ[¿µ¸".=>fA8]à…ž"E ýÖâŠyᡎï<Úuƒ»y~‹qæxÊÛ3¸gïfý®,ãûœB$± ÔªUK–,Y¢u~Óª‰K`Ê^L.ž»$Ë|•BÅ K'ÂÂdH’5H::!d`hGHWŠ9¬Ìz-Žûóÿò­aŸÚUŒ©`WñJ›Jñ@¨Äkמ´Þêúë/¿âËC<,­ÕÌŽåðC€w?Kz¸èµk·ŒÁ µöd£ïN©^³•ÝSá9s—Jë¶}m ÖЯàš×|ÞUeè.Ò©c3{êUÝì9KeùŠ6íoß¾“+}åØñ3²jùOšç¹å|O´W¥ê5h/¾›vÙŒ}¥ÅB^¿a»šcÍIa¯_¿-_•¬#7oþn3½2vÜ,íèó²võL§=õí*e% @˜'o;x!ÛÔׯ__ä6l¨ºøúújySNi¿Ãd³ïàFÙÔ#žxæÌ™Õ'mÚ´6žÔÖF°iÓ¦ÖÕ.¹÷ϰ¬+7‡{Á]x÷c?$…a!m`ÀAû¯V­š>Ä¡«Ùè ƒ¾nÔhðÓ§OU³7iÒ¤ hˆM›  ©S§¶i7W˜Ûõqð®D2W]âêßáHpö®ÏË+ @À𳩜ï¼NãÚR²lq›7¼íx«9¬ÌÕË×äw-·oÚ—©XÊBQᯠ)ƒ;B¶¬\²Z|j}0È£ãÝ;w¥È%òлëØ6‹ñ!y–Ö’\8WÈ ÁÝÅœ5g ±wÚgÑ7Š Ü¿ÿþ‡A#KætFÙ\èÚ}˜2¶'K–DŠÉ«<ÍSx&Ó’C]–æ-{±,K—*,ßµj(_j¡\Μ½$3g-VFt¼Þݹë’/ïZR¨ÜfÕFkÁ–¡CºÊWÅò©W¦¶nÛ+={Ò^9~!07iÚEXmqšÛ¥ÛPÃØž5k呎ùöfòÔù¯yxå/UO®]Þ¥Åõ;T˜5g±alÏ•+›ŒÑKû£6­ª[»n› 4N­mÛö}òË‚•Òô›ÚÆZY    € æ9¼§õ°)06Û3¸GUFŽªPßf«béƒC$CEX’ºÂàŽØôH0 )S¦Œ2Ê«¿ø…yóëœ(Q"I–ÌöµykUq±_ÄPwFÌ_æìcn×ÇÁë‡5:#ðñOÌ{4÷1×;»÷ÿ³wpNmE”.½÷Þ›ˆô¦Ò‘.Mšˆ‚ˆôÞ¥(JG”Þ¤ˆ R¤ƒ *ˆôŽ‚Ò{ÁoŸ¹o–M.É%w›»äîy¿\¶ÌÎÎüw“»{æÝg¬õp™H€H 4g˘Ð%ìßbµ•Ñ¿£ªÖª¢&^·ž­{ÿ®²hι}붼߮›<üç¡Ô¨[ÝÐMž‘=;÷ÊÈA£•ØŽcÚ¾ãø„–µžÈX¦¶Fž#r Pp÷3oý‡­ŸOðÕ#“fÿþ'Ý¢Áœ¸tùŠ‘ù½I>5i¢›6q=±×‘#'¥Ñk5eÆôцwh|}ˆT®ÚÂÛÔ¯.óçŽ73°2eJ/ÕªVw: ©Óæ©c:½7H æøgÃ9pÍ6m˜ï…ž;w6)kô¥Ë54|0ï¾ ¿«l÷úõª©Ã±>sÖbµœÇð{ß³k…™‰žÑð,+]º˜1ÙF|™6}¾áßyWÆë°ˆAü°i—zÇ®]Þ4&äzQ­§L™Üxì· œ>ó‡|³jƒÚ¶ný6 îŠ xKÀ*¸_¸’µæí±TîÌ™3›sîÜ“§%­Ùî8ÙþZp_±b…Œ=Zy¾ë ›6mª½~GÆ¿ŽŠ+šõëmžÞsäx’\òǸ-Š>>}Zí‡m<æ­>ózŸ» N:eîÊ™3§ZÆßºàsøðaµ®˜(ÖU¸c‘¾»:·‘ @L'àOËoØZmetùºBë2)S¥'Ž”.†Øm£ãå©6O©DÅèC¥IËFÒ¢íëæzT,S[£‚Ïé_±ü[}̫ݚEózï¾Ç©‹«éð*úBM©R­¥²Kyð Äs7Žêí ¦[kEfûÌ9ìGÆ92Ð8~̇}M±]‹,F ïaN>ºoß!Ù»÷7½Ûá½aƒêb»ÞY¨P^Cìn¤WUÖ¼^™0i–šœëÆt5Åv½ïØõÌä)³Œª;jwÆ iÍb¨Çj­ƒèïå¿V¯ó&še¹@$@$@$@a€˜kt3gÎÖ!»ýÀd¨®ü[¶l1w9Û´(PÀœ,Ö2ð¾‡Åv3•+W6õvA Ø(ÿóÏ?ÿbóÐGCØž={¶zýôÓOª€U4ß¾}»š .ô‘Fv á»_§Nõ¯ˆÜ¹Ÿx÷îÞ½Û˜ëÈõ@ĵk×dƤ g=§ÕjF{¼£Œs`’YW‘¾»ªÛH€H &ÀF¯Ö¯)sW|%ûÏü,CF´ÝŸÝ[®ÚVF—Ç„¡˜0ÕU¼fxɯ۵FŠ/¢4è`ZlO“.šLuÜ´MýÃU‘µ-˜ÚYLxžÈ!À w?sÖ«ŸO-ªÏ`Ïóf3&¼*î¶?íÚ6 5qè¡ÃÇÍòÈ$Ïœ9½¹n]Hš4±Êt×þ쇟 kç¨YÃõ/”kÚ¸¶Lœô¥:äØñÓê?~;pÄ\Æ5ß¾=äŸ)sãÿÐÇsçΛÖ4 ä6&|­. †LfŸùœy*‚AÃΦ¤`bQL¶šÜ˜ œA$@$@$@Þ€]È&cÂÔ1cÆ8¢'ÍtØD+ƒ RâarVkŒ9ÒÈ´ If@ö6&ud¹ïß¿_mîÕ«—1ïÏ=µ ïvøäº ëÓ·nÝr(‚ÉNaÏ_|x¢Ï;W´_¾.ËdÏkQ\ Øùòå¼0±.ÊŒ;V¨Sï;wî”={ö˜ÛªW¯®–á¹^¡BÙ¼y³ù  _|ñ…C Ä!C†˜}Ä€C‘"OþîmÞ¼¹¬Y³FÕ7kÖ,Áº5ë;`¹³~ýzUÆùGDúî\×I€H ¦ˆ Ëoï<ðdà:¬òù æ•ïw¬VYî‡~;$W®\•¬Ù²HÖY\þNí9°›àå)Úvl#x¹Š©s¦^®b臃/wák[/=t=¿ž»ú¹\ àîŠJ¶1ÃÝ5<üóVÛ&¡v&OžD 3l0<ÏsæÌâðB¨ÂƆ¼.¼Ýáß®#{¶LzÑå{öìOö[…zká̆»°ÌzXå «ÞÚ††Þqw¸ÃvL²Š€- ²òûüØðó| þqÚ½{Ÿà…€€_®\qé×§£T~¥œÚÆ$@$@$@$¹F.A`RË7BþÖÐÚ´i#^ƒ9¶mÛ&¯¾úªtéÒEe¬;vL,X DxݯÎ;»œX®aÆqúŸþ‘‹/êâJ7Wœ`㢅ö>ø@š4i¢êFð7àˆ#T{pÚ„ÉZqMàk¾cÇ™7ož)¶C`¯X±¢:ŽÅ ²×ãÆS× m„ ŽLx«rÖ§Feü}XNY*®ZµJ5j¤²á *¤Dü9sæ¶ëøðØ@¼/Z´¨zbO`bÙþýû6ˆ¥ ûÄ»*3Ǹ‹ˆôÝ]ÜN$@Ñ@‚„ dë¯?DY»?øÂê÷…’ÅüQµíuS[mï<+ŒtÜýŒœî!€ãÆ#“& ‰0m«ýŠ®ÌÊ8¬ë~ëqº®°Þ­“qAlÇ õ}è§(!˜#=o޼·ª'?ßyçw;&¾uô¾õåË—õ”(¬pŠ/®2Õ;vìè|¨ZG{!Ò×­[×°lŒçP&A‚JÐë­·¶;¯D¤ïÎuqH€H€H€¢ f¸Û|%wç?Èm>«3äÍ“Ãä°sçÏjÒѬY3šÛôÂÕ«×廵›õªqœkÁýëekåÖ¯™å¬ K—…x]b[ŽìODöܹ²ïSEwýø‹d·ì³?wÞreƒm-[Ô7~$™¦Šà^éÓ»ƒú‡ Ë Â4´nÕ@2g+kxpÞW ÿôÓoÆ?VŽb½õ\&  ˆ¾ ÀâeGØU—]õ¸êS•*UTöµußêÕ«ÕD°ðc‡;|Ô1‰§7w# ]ÊCL÷ÈfŸ:uªz]¾|YàŸýl®Y´EózËË–¯UëÃGL’:µ+ÿ %0÷cáÓÏæHÇwªm¥J=/o¶i¬2°>›6×ø§ì’Ú^¢D‘PçM‘"™š4õ?.¨2éÒ=ÉxWøƒH€H€H€b 2^¾†ž´ÇU®\Y\e»«3EŠîv™Û!®»²o1 xXˆÈ±°Ž?x=ú‘ŒwWYúšì°+"íw¨ˆ+$@$@$@$äh)cód†»Í@½¬n¸A¦Àá»F­6²bå:9{öOY½æÁD¦ŸÏXhÖ6iü‡‰£Ìÿ_¨UûMþÁ$ùùçF¶ÏI™þù)[¡‘‘au]•Ș1¼Þ,d’+l¨[§ŠT¬â‰ò%Jד¥_¯L¬ ÿõƒ>–n=žø¦v3<ÛuT~¥¬^”æ-ß—Q£?•üÕ˜Ìë² c¿ÕÝŒ,®±ƒ 9rd1ËsH€H€H€HÀ=ÿýWeÃcbPˆíVÁýí·ßv ÷ @8 0Ã=œà¼=Ì›G[½­‹åÜ€û´ÏFH§Îƒ”Çùú Û/ç@öÏÈz“<½à¼Ë\oݪ¡Ìúr‰ ü‰z™;þ¿$És2{Ö'¡üA§~úÔmð¶1™×qõz­±k¿ÌáC»IýzÕÌjÇ}2PvïÙ§„ýË—¯Jßþ™û¬ Ï=—Hæ|9Öº‰Ë$@$@$@$@À¥P¡B¡J¼üòË‚IX$@$@$@$@$`7 î6uÎp·¹zVç„ò²†Þþþ²eënåw®‹‡LUH>4\ÙÉèí®Þûõé(9,ò£¦È;wÍ"< ÍW3?6|1Cû¿çÌ™UöîþFzõ%3g-‘Û·ï˜Çb66ûw–zu«:lOš4±ü°~¾Œþè3•I÷î=‡ý˜˜µ^Ý*Ò·÷;ÊÓÝa'WH€H€H€H€|"€ÉD'MšäÓ1,L$@$@NsÄ9x4ЛÉö‘@Œ @ÁÝÏ—ÙÐhcl¼ßåMÁ+¢qêøV¯«€ÝÊúï稉£<.ÇŽŸ’¬Y2Jþü¹Œ‰£â¸­gÏ®ûúâv·®m•µËù %Y²ÄRâÅ"‚,sOsŒ;Hµ~òäYÙÿÛa‰?žð³eËäöÐ4iRÊØ²ß;-g +L~•!}Z%î#«žA$@$@$@$àøŠ1BM¼š A#¢°”.]ZÐÀ   è@@;+ܸ~CÊ®ºmû“5Âh{QÝtŒ‚»0áÝÌ ÷ð’³÷8XÇ £¯ðþ+WîÅpŽ_xÙ³gV/_*H ¾)’O½|9ŽeI€H€H€H€Bˆ/ž´oß>ôn!  hB ›ñ´}ÙM;F²@IDATŠeäÐCѤGѳ%Ë–ä)’GÏαW¡Pp…ÄÞ z¤ÑÞZY @L';vlY¶nqLÇÀþ“@@ˆP­‰ùï¿hÐ vH€H€H€H€H€H€H€H€H€HÀgÜ}FæùgKf¸{æÅ½$@$@$@$@$@$@$@$@$@$]Pp÷ó•¤àîgÀ¬žH€H€H€H€H€H€H€H€H€„w›/„s†»ÍÕ³:          %@ÁÝφî~ÌêI€H€H€H€H€H€H€H€H€H @ÄvD›f0Ã=Ú\Jv„H€H€H€"L S§N’ A‚×à H€BxôèQèÜB$@$@$@QL€‚»Ÿ/3Üý ˜Õ“ @È–-›Ìóy[.ÌŠ¼,ðì³ÏÊ{ßð²tøŠ¥H‘Lúô~G²dɾ ¢à¨lÙ2IƒúÕ¢àÌ<% €·8iª·¤ÂY.²'MÝ¿ÿ|»ú9vü´<ûì3Ò°Au©T±”<ýôÓfþùç¹uû¶Z¿ví†è6&HOžyæŸËáo΋r·oߑϦΓ_~ý]bÇ~ZÊ”~AÒ¥Ke,Ç–jU+ ˆ÷ï?ÆL“}FŸj¿úŠÔ­SÙèÓ³æ~,ܽ{O¦N ©ïÁƒ¤h‘üòN‡æòÜs‰Ê¡®oVm#GNJ¶l¥fJû½]9zô¤Ìž»LR¥L!íßn¦êcd̘V2dH#Ó>Ÿ¯¶ýþûQ]z‡^¾Rãÿ÷e£´hÕU÷Ç5ËyËeÏž}R p59x踀H›6¥ :^&Lœ%wîÜ5ëã €w˜áî§p—ÒÙãá®ÀÇÿ8³Sàƒ®^år |Om*P ·\¹ø‹lÞò£Ôkð¶üõçn3ÃÝšMím9}ž°Î‹r­ß졲¼OŸØ*™ ŸvÄ!Cì…€üèÑcµnýQ³ÆKòù´QjS­šßJ»ö}ÙÝ:˽m»Þ‚Žƒ¿}/ ÄWåú÷í$ÙsU”·;ô“ukg«m­ÚtWôGm”9²(1yÀ OdÜø/¬§ój¹hÑü*³}ã;$[ÖL²jÅ I“&¥LÀyšµxO~ßÿ½Ä‹Wš5­#Õ’Ùs–)‘ÿ«YKÚ•Õy>|h>MÐ̘ 5GŽÌ²î»Ùfßúõé(¯TmaÔ×Eöý¼ÚlÛ·${¶Ì²fÕ,5È€ ˆîM›w6Ïë-d¶CÜŸ4aˆYÓÆµ¥SçAªýæF. xE€‚»W˜¼/•î872Åay‚,g2Ý>!7nܔĉClG`?’äÿË©R¥pÛ9oËysÞË—¯ÊödЀ÷L±'Λ7‡|»r†aqs'T;Úµmbn«T±¤Ü¼yKõ¥aÇrïÞ}è šI“¿2Ëa!gÎ,²uÛù÷ß,ò¿ŒŒôŸe¸AJlÇ~ˆóæ§|:«áŽÁÆ Äv„ü^=Ú«‰hwïÞ'*”p¨÷‹ÏG›b;vhëžÓ§ÿd¸ûͦ؎ýTèkøÌרõ† ŒÕom×Ö5éÒ¥–ÝÛ©r?îþUJ¼XÄ+.°ðªVo©®CŠäÉ”EÍ’ESŒ§ øð ®ƒH€H€H€H€H€H€H€H€|!@ÁÝZ^”uÜ#3Ã}è° òÁÈÉ2tðûRªäóJ ^óÝ&éÚ}¸ÊwÕ|´×›6z*çÍy/_¾¦N_°`îPÍxщ]…5ããmÙséÒcù?#ãý¡œ<õÄ>eòçË)5 _sˆÊÚ6æ…b…°ËŒdÉ’Húô©Íu_àwž={&‡ÃŠΫÖÏ_øÛa;V²fÉj6 øí;GJcrWÊhÁç…½5àøë¯Kª¬7\PƒÛ·,Qî;vg¶™úg$eÊdrêø‰çÉ“(Ï         ðL€‚»g>Þë˜á“ü¿‚%_¯Qv'½{u0«•@vqTEÙ2/ȦÍ?ÊO?í—_=(#GM‘Ömz¨æœ=û§šDU·-kÖŒ*³}Üø™*3zÓæ]j"Ð’eê D]Þ”óæ¼O?ý´ ÚÍy7Jç.ƒeÿþCjâÔñÆ„§ð\ŸõÕ}JùûïËjù¯¿/©,v¬\¸pQmCö¸Î¶ïݳ½,Zü­Œ7CYè\½zÝ£×}înLœúŠ*ËœÍëÉç3ªLL:qÒ—Ò¢uWµÿò•«Ê^­øð–+» ‘^}F© QÛ¿ÓOFô™Ô«[U ëLwÃ_ýرӪÖ3ÿcÇN©|èu`1K–®1ì]>V“½bÂWLÊ:tøé×·£Xxôè‘‘½~UöÞûCŒÉMÇ)‹ž½FO5LR~ñæ1Àã Tôæ]jÒ×…‹V <åÁõ×}•]~¢@·“ï$@$@$@$@$@$@$@$@$@aˆv–ˆÈÌpïÚåM9x蘼Xª®jr±beñÂÉR¿a)R¬¦,ÿzšÔ~5Dˆ†÷ø—3Ç(ÑxȰñª|‰E¤]Û¦*]÷Ù›rÞž÷½ÎoÕ±•˜ ßuˆÖhc##¿[×¶ê”Í‹«¡–«Vo%{w¯ˆþ…ŠVWÛ^­ÓVÙ  ­ÈäG•n=>Pûá}^³F%™:åIÖ;&…˜ŒIRñJ”(¡ŒÑS&MùJÆ|<]R¦H®|ÐU^þ@fýÌçþDyÈÆåÍeðÀ.ª†û÷Hî|/« Z±¡ÁkOž:˜2i˜ñ$Âëæ™ú÷ë$ Æ—aL’FLVÛŸ{.‘²êò^µŽŠŽïTé£Gö–.]‡ ¬| Œ¿Ý®™|0¬»YŸ·\pÎǺöS¯b=yò¤Š«5ƒÞ¬˜ $@$˜OcÆ Æ`Þ=)X° aÇ•=µ„ïÛ·os6–ÿ’dÉ’Iä¹çBæ3 _1ó¨[·nÉæÍ›ÕÜ(eÊ”1¬ÇBæ/‰™4ìíuT~>ìíIÔÖvðàA9zô¨jD‘"E +¾,QÛ (>{Dî«-[¶ÈÕ«!Ièy†¾˜'Nœß~ûM͹T½zu¯ì9C×Â-$@$@$@Ñ™w›¯nTf¸çÌ™U6o\¨2ÖãÄyÖœXóú•}.{Ùüõz‚²Ç'Nä ´[«œ·çÅàCÇwZÂt 5h²d‰CÙÙÀ[ýÞíÃÖÓ«eWÛ5ßǘX´—‘鎉EQ† iÌ Iu%Ø¿šõ‰@èF_³gϬ„úí›ë"áz¯R¹œàå*âÆ#·®pµ+Ô6ô£[×·¤ëûmåìÙój¦Léþx¯S»²<~øÄ¨e‹úê:§J•<Ô§ÞréÓëÁ` Äý?ÿüK‰ï™2¥Õ>n  ˆØ·oŸT«b%öÆoÈ_|‘ê¼>vĈ2jÔ(X¬‚ÿþýûõj¤¾ÿòË/’;wnãwm|¯Î{ýúuã»ùOÉŸ?¿WåýYè믿–Ö­[«S|ôÑGÒ½û“A^ž÷æÍ›ÆÀr¼P¿×Ã:'„°Ô©SKªT©Üýûï¿y^þ0þ‹¹-;¢êó}‹Ìsàþܳg:e§Ndâĉ¡Nïëg0TA´!¼÷>s/½ô’àÉJo¿ý¶|öÙgz•ï.]ºÈªU«‹3gÎH¦LŽó:ùž”ýñÇ¥\9×ÿƒèsoÚ´I^|ñE¯çèãøN$@$@$`ZÊØÃÑm-‘™á®¬ô¤IëÕ0ßÓ¦MåVl·V9oÏ &ÈZ÷äo=oXËÈrÏ–-“ªžäî“°bpå1À%sæôêåÍ}Þžú àà\2¤5þY Øˆ÷ÛDÁN‚æ¥K—T†yd J¯]»VúõëgX|–/¿üR¶mÛ& ,Ñ£GG Îû÷ï+ácÖ¬Y^Ÿ̘1R©R%¯Ëû³`«V­äÀAäȺ†—/_6~G%–nݺùÔ52ùàÉTdÔO›6-Lr(S»vmõdR˜…Y€H€H€HÀvÌp·idþÑesÓclu¸fV_uO Ö~¿Õ°`é/×®ÝTi_Pbõ§“‡…šÖSÁ²Ï.°òñf XúÎv’ ØK EŠjh{ku_ÛÆÕÎqãÆEy3òàÁ%z\¼2‰û–?ÙsçÎ5Pïâ@ø~EÆ8"²Ú’4iR5¨œüØ•(« Y³ºOªÏg0Ê:cÓ‰Ã{_%HÀüüxJì°©™AYMìØ± Fï¿›ìê$ž0Âï·;w†Yeûöí¥B… 2}útéÐትe˜² €-(¸Û‚ñI%΂»‘<Ìp§N“JÔöª•›7.eK>3ìWB&Çë)ÃÛ2ŸWÇ[!_¸ìÙµBYõ[Ù^ ¨!+ŸþY‰·ðΘ1£CCöîÝ+xʼnGš7o®EQÂhΜ9•§°+kø_¹rEù¶c 0Q¢D*S•£.»ûGæçO?ý¤<£ÏŸ?¯ÚߨQ#É“'C?œWœÿFpÞ‘uŠ?üðƒj²Sa/öüóÏ+Ë®âîÝ»‚LMØìÀz'W®\Ò AÃ~í‚ÀÿÖ®žøxbþø8¦mÛ¶¶xã#«¬‡P'¼âÓ¤I£Ä¥ &O³e“>™³ÅU» ,Á*Þl!êÔ©#3fÌ(A*¬Ï‡µoø\À:÷$ÖR¥J©‡¯¾úJš6mª>Öòá½æÖ:\-#qañâÅÆÜ5wÔ —¶êÁ ¬7à)!mrqqï.Z´H`ƒ‚A|Nñ½ðûï¿+;*,;êƒuÊàÁù0HVøó3ˆs£Mëׯ—C‡óó$TóF4nÜXõÝÚ6ø¤ãóƒ6¿öÚkêçú`_î+}Œ·ïh×Ò¥KÕ“0ø~À÷4¾Š/îm.Ëaî%K–¨9@`‚{áôéÓêûßkXÇ@‹s`Î |&yóæUƒk>Tß%x ˸.x Ê8ßÊ•+ÕõÃï=0W¨P!k±PËè?¾GñÝ‹Á>”7n¨rØ^VxJO>¡n<V”/_^=í3räH5°ˆß‰    È#@Á=òXóLJv4W/ý ­‹ºf‘KÔ±ç™I º€`ðÖ[oÉçŸîÐE¬ÈFךÂúâ^ƒ R~Ûú téÒ©¬½5B&ÙÆvU2dPÂŒ.±ÄË—/W«u›¯Ëš‘EGû!è£ÍðZ>|¸¯kÖ¬iV ñã?6íM>ýôS%Jé°ZÇ@‚à}êÔ)%z£|ç­hîܹÖMjâ:&ŽD@˜>wÀ‚¬³È ÁÜaÃ@†0Î £©àì«p}0°,O3yòd?¶¾~ÎÇx»Ž¶ ¾=zÈàÁƒã¨Lˆ—Mš4q[®?Dy”ƒå‚7rhs RÞ~>Ð7”ŵÂ=Y_?ìK’$‰À2YVá½æ¨3¬€5>Û,_}õU%ZâˆíxŠ@X Òè€8[¶lY%¢–(QBYO­^½Z0…€èé,¸ÿúë¯Ò²eK%nëz0ÎáëgÐùxo×qßbn|ð…>ÂJëÚµk2dÈemU±bE³º^½zÉ®]»Ô:çÌ™£!Æ~òÉ'ê3Ö³gO³¼^ðõsŽã|¹¯ôy|yß±c‡ú.Á¤µ¸˜{æÌ™ÊÖ ß1ð÷dùèé\¸h×®êCÆ Õ½íüû–WãÇWTº®¾}û Ú…wˆöµjÕ2™c;˜ã¾ÕOí|ÿý÷j°‚~Ú´iUûÔ©SÕwÇŽjàõlݺUp­0P©#}úôê÷œž¿Do+ ¢aPò½÷ÞÓÕ…ùŽA…Þ½{«¹SñÎ   ˆ<±"ïT1ãLÆÿ<¡ÿˆsØÈ  ˆ VóÍ7JŒ8AÙÈg ¦è€Hñ±žÜkÖ¬QYÕ ®B<7»ˆ¿ƒ!Ô÷Ït¬ã…¬ÅªU«êâá~‡ˆß§OÕvˆiÈE?p~ˆhÖÀÀÄBdf" €–.]Ú|!KÂŽÔQ @•¥¬·#cÙzÌ /¼ ‹;¼CŒž={¶ £øÄ‰*Óì aÒSkœ={V1‚h1 ¢&2Ú!Ì#;ÙîXwß}÷@à :wî¬D{ pÂ}¡²Y³fJ¼… ûÈÈ`wv1È€ûÂ]f©ó±qOà8øþGuxûù@;!(N™2Eµ™áºq]1@Vë5ŒÈ5W•…ñÍ8ÄV|>u´iÓFU¯'K†økI“&©û6ðaÇ÷^ "œ3ÒÑdîâs‡{W?mQâ.2ì­áëgÐz¬/ËÕñ…Ï!2×Ñ. &à{×£~ýúêúè:qOwíÚU­¢ßÈǼÈÌÆ€Ù`cÀÉ:á³>ΗϹ>Æ—ûJãí;úˆï[¼ãiÂÈôÆ5ÄÀ$®»¾–ÞÖi-‡\S<½Ñœ ®c‚\|fñdæéÀSÖSÜkøþDÛÊ”)£4pa¿ðÒÿ§Aد[·®äÃ5À€žVÀw#ìXðÝóî»ïZOa.·hÑB‰ú8'žÂš?¾ú}€a|ÿêˆ(+|ÞÎ}Õõ»zÇàÂÓw§«ã¸H€H€H â˜áq†kÐÈy,Ä$@$@$@à„m‚5k"+Rˆ(Ú!  ´ 2¾!´è€uÄË/¿¬¬ ÖÃRBÄ„Î̆]²}í ˆÿx!Ãö%:óçϯl ®ÁZC?ºÌ]¼`“ìDˆÁæÜþf1b„Úýþûï+{L|çÍßZ§ ¸Ç‹O:uê¤ìR ÈZ40`€6¡½ZÁ±°s8ºËHE_á#¬Û„§t†;ê°# Zað À ™¥ =D>'| d¿b@‚›•…/uØUÖÛÏÄ\<‚ÏD Ô 0 £'r6l˜Ã5ŒÈ5÷¶xŠÂùI ‹{Os¸ ý¤„ÕeñÔ¬3 ä["5„}™Quà{O7àIkøú´ëí2žF¥¬p'DøÀ=…'ð9Aæ:®›}ݨylÇ`lw`„ëì<ÀæËç\ŸÇÛûJ—÷帆'NT‚µ>6/LùöÛoÕç·ÿþ^Yþèã­ï¸§ð}^zRd½¿J•*ê)d½ãsl£qáX € Óß%ú»ƒ†ÖÀ}…§ ð;OZèÀµÄ÷Ûi#ëÃøcÝr™7ož>D :áÞÄw%Êë'" OMàw]²dÉ”Íy²0òå˧¬¹ÀƒWú{;ŒÃ¸›H€H€HÀÜm€h­Â9ǺË$@$@$“ TªTÉAl×,ॠÁÖZp×ût¨^Ç;D8d#"“•È~Œ¬@Ö#„ȾF@Ä€0QYª‘Ùœâ?Di\†th‹dâ[Ùµ°à¹æÖºí^†@Šì[<í,|vàÝwØ39ÛÉ`žd=[ÅvÝ&Xm8 îzŸ?ß1ß>ûð’weƒïüö]žæ°†î²«­áëç\ëí}¥Ëûò¾{÷nu8p@:vìêÐT©R©ïXN¹›"ÔAn6 ¸GœTÜ‘™nÜu9ÜCÄ] é2øLá»ÆYL×ûñô¬ŽPι žòq|â©dßã©#pˆ+<Ù'G|ý~C»ð‡AW ÁvA$@$@$9(¸û™³õŸR?ŸŠÕ“ @@Ð6)ÎÔÛaíázŸóv-”B`Ž,6*}}‘ ÄfdºÃ«v+Qœ= ¿n´K?5€ÁEÎQ¶»ð4ù««LeW¢¯»ºÃÚ±~Éȶǹ`‹±XÛ¹;סï w圷ëû “Fuè¶8·Coןío×UàoO<bˆ\sk=v/C„ÅçFðf‡Ç5Äk²¢‘Ñ­³öñTDèråʹlÜ=™áò›6ÂÞÌ{Íì\5&u5À…r:ëZãîóäëç\×§ï½®ßõv}_éí¾¼£ïh?²ñ]ÎïˆÙ Ý^çzôgÞ:Øh-ƒ§œ<‰íxJ ŒÈqú³¦?{ÖrîÚ¥·ƒ/÷ˆ° ï÷Ú‰v@pÇwwë•ã2 ø—w›ù2ÃÝf ¬ŽH€H Ú€ æ*´ˆ+Q™‘°'p-êl9á\ÎÎõ… *ûø[­`‘jw‚»¶Ç—µ·a=F[T¸:â$¼¬‘ skÀÞ•à±,\ èÖ:"{b£¶ÕÁ¹!”iÿoOmÁ¶ôÉ—Ðד=z˜XQ‹n°²s°ÇÛÏ®+"ÚàMDÖ5Ç} ~çЂ¡óv©„îOXp‰H€H€îß¿/È ÇcûÈÒ{÷ÝwU6+5ˆ»˜õäɓһwoåãìL b&~«W¯žz,Ùåœ!pÁ·×sçÎXg  ¢²KÚ´i!‘¶ðš‡9ê‚}„4Lਲ਼Ðg̘¡¼³­öÃa÷!“èÁ–Y‘4€¯/;ÝfÕXãÄæÈš…‡9{x#“"ê‚ø qâ*Ö[·n­&]D]kÖ¬QþöÈ`‡à† 1q Ö»wï®Ê3FeubBUüJ1é#Ä£?üP7E‰§`‹€h!>Óø›“›ÂG׃¯¿þºKñ߬ÌO Z½äKœ9sF÷FpÇu†Œ€_µò†çók…û 6;ð§ÆÈÇuÀÄ‘¸G2gÎì0ᨯ×\u2?pßbbKã-Z´PB+î=|.˜üßú©åÈ,Æ©˜XŸÜWk×®Uó$à^³>E ­›`„ó`Ð b4ê…W6÷;>—;u„ç3¨ ë×kâ{OÜ@øÅ ŸSý™ÅÀ&êÄ÷óUè6õø &MšT=y±±bÅ åÙ§|ýœƒax¾w1À¡ß»\XSá{QXãèŒî^½z©ïp܃˜pÂ5îE ö ïè'¾»0™±S…ã|ýq¾åË—«ï ÌFÅý€{ß+è;>[xç×íÇ~ü>±Îo€m°-Â=ƒßC¸1—&òÅ÷4æ@=ø~Óö5ðíß²e UB=®¼ø1†}øâ¾Å÷©Žˆ°Â=–:uj= ¾ƒ¼ìCYîøýgýé6ñH€H€HÀ(¸ÛÌÖYpw•qfó)Y ,8:tP‚,Ä8,Ct‡¨ƒ€ˆÑ²«€pŒý”á ÁÂò¨Q£”¡°€ µÝ¶['Ù„Èç oÀ¾cذaJ€ÑÖðÇ…°ÁB:X[Ìš5Ëá4˜´b4²ó§M›¦öA€‚]Ä"ç€?t•¹ê|<H°ŸÑYµÎe|]ïç×ÞöøÇ;ï¼£²Ïür’H¬ÿÌ!«òÕ eüÜ…‘xfž*&صy“4«ú²äΛOüÝo]GV"D@wYwú~ÇDxÈÖ ¤€ml- ¦@ô%00€>¡ßVAÔS°ç€%Îå.³Î!»mƒ¨î­7;2Y!úbBJˆ~Èê Ö€ˆ ¼¼ý18ƒI!äa"¬À“¸À D>FXŸë¹qíàÉñŸ¢ž"2®9²Î1I$î)î¢)^È Æ½‹'àWŽ ê°>ÈèGyÌýà­?ڞϠ»ö»ÚŽ(XE¡ýàóÔWÇ{ÚÞϹ®Ó—ûJãí;¸â;÷#îCLjW . æÁžÆßϾ£qñä =¼ X'!×ÜÝ÷´®'<¬¶oß®žÀ€žôð&ðDB Òë§+¼9ŽeH ˜ hÝgô„Ò¦Ck[ºR¦P9zè˜,_¿DÊT(mK¬„ì"P±Ø+òûþƒªº /——%ß-°«jõäI[@®šå¶}›$w¾ë3¸ÙyäàQ)[¸¢¤L™Ò|ZÑMÑ`Ú|×”OÀ w›/Y ¿°™«# ˆiðx|°뤩¾ôBLŽ9|9DýÖÈ´ôÆŹd¬FæÄ³Îç·s}ðàÁJ8G–¿5£ÕÝ9`¿ÛW°#ñ60hånÀÎSáù zªÏy>|¹ÎÇ{Zïç\×éË}¥ñö\µµ“·Çb9|>0‰*^¾º¼=&<¬`ã…§ ðô&–kPþË–-<ýC±Ý—+ɲ$@$@$`XöTÃZÜÀÆ    ï 8pÀœðqþüùÊžøÛ1ñž÷­`É`#AJ[é„•,jø:ÊÄ»<Ñ€:a‹‚¬x €ÿ À¢ó|ÀŸAm×ã<ç…ÿ[g€å žàMXÏydéc® @ä`†»ÍÌÃúÏæÓ±:  ˆvà¬'Ô~Ü:ë¢jD¶ °Vð%!ˆ¬]†g°±ú8{.²Y·ÖIf½9Æ]x;Ãß“ÑÂß]`RGd†C”ò&9Ë°Þ fËw,œ·ÇÔÏGTß»Î×!º­‡ç¾Â¼ðÇÇÙŽ;Ô \ªV­ª&9nŒÂê&ŸÅ¼K—.UƒžÊýõ×2vìX5‘¶§rÜG$@$@$àÜýÃÕ¬• î& . €W–/_®üœ­…aåñÅŽÀd¢z²LoëÛ¸q£š ÏÛò1µ<Œ[“ÚúÈ0Ÿå4ɬ/Ç[ËÂÿ“vb€ÆS`à`Xå¬uÀ<&DLý|Dõ½Ýï­ðÞW±N€Š²˜<ø‰9$0ylX p1O ƒH€H€H jPp·™;3ÜmÊêH€H€b*È>öWDÆÄ{þj{ ×‹ [}}zÀî>y+¢{[Îîöz}1õó÷n ßi_Dî+»[#Òþ@:Ö!Ý›2Ô'¶…H€H€¢Ïé?Ñ­·‘ÐÃÞÓ!¼yLÙá® % îAyÙØh          @#@ÁÝæ+âl)à w›³:         PÜý|a(¸û0«'        F UêTfS¥Ii.s!ê pÒT›¯s†»ÍÕ³:ˆ6Ž9©ú2uÚ\™þù¿ôëñãǪÞÏ>ûL¦OŸî—sDf¥=R§ûûï‹‘yZž+†øûï¿äÙgŸ¡½g·I€H€H€H€‚À¿ÿþ«šÜ§Ké×u -Í×u6¨ÚX˜Pi RVb#­AÆŽ[2dÊ`cͬ*¢(¸G”`Çó 9 @Üc ¬ß°]õýñãÿäñã?Œü»ßýuŽÈ­×ivæÈ=9ÏS·ÙÇcJoÙO    hBÀÿÿéä§h‚ˆÝˆf004~ôDéÚ÷=‰7n4ë]pv‡–26_7=ºdsµ¬Ž¢dIG»>±C$@$@$@$@$@$@$@$ÙÒeH+qâĉìÓò|n0ÃÝ »63ÃÝ.’¬'º¨[·Š|4fš,˜G*U,é—îíúñÙ½{Ÿ*TH*Uªä—sDf¥ûöí“M›6Iüøñ#ó´òl1 @xœ?^ îµÔ’6Z‡§ŠPÇlZ¿Y îm;¶QB~¨Ü@$@.ÐRÆ”ˆlr¶”a†{DhòX         Üý|­(¸û0«'         !@ÁÝæ áœánsõ¬ŽH€H€H€H€H€H€H€H€H€H @ Pp÷ó…yê)?Ÿ€Õ“  î6_f¸Û ”Õ‘ @ àîç Ew?fõ$@$@$@$@$@$@$@$@$@$ (¸Û|!þûÏæ Y @P ànóer¶”a†»Í€Y ( îzaØ,          à"@ÁÝæëÅ w›²:         Üý|¡h)ãgÀ¬žH€H€H€H€H€H€H€H€H€„w›/„s†»ÍÕ³:          %@ÁÝφî~ÌêI€H€H€H€H€H€H€H€H€H @Pp·ùB0ÃÝf ¬ŽH€H€H€H€H€H€H€H€H€‚„w?_(f¸û0«'         !@ÁÝæ Á w›²:         Üm¾P΂;3ÜmÌêH€H€H€H€H€H€H€H€H€H @ PpÐ Ãf‘  î6_/ç w›«gu$@$@$@$@$@$@$@$@$@$@J€‚{€^6‹H€H€H€H€H€H€H€H€H€H ¸Pp·ùzý÷ŸÍ²:            @ÁÝ—‰¦ú.«&         #;ÀÚôͱÛÃýø‰3AÏ„ W.]ºêj3·yAàÖÍ›rìÐA/J² øNà3§}?ˆG (Üm¾¬‚{D2Üõ±uë·³¹…¬Ž‹€¾×«UÙÍjÓºïeSÑ‚ÙH¶*ÚÐ÷[´é;"ÿþû¯lذAîÝ»' ”ìÙ³G•Û·oËáÇ寿þ’dÉ’Iä¹çž‹´óóD$@$@$@$@$@$Y(¸GiÏÓæ×ä‹™‹Å*àûX‹“@ÀH žÔ«[%àÛ( |ùå—%_¾|réÒ¥@iÛM ÄŠKÚ´nM{s»µoß>©V­šðÆoÈ_|)0FŒ!£F’[·n™çƒà¿ÿ~s=¦/\¿~]þüóOÉŸ?LGásÿûí7I:µ¤J•Êí±ÿý·üñÇR¬X1·e¸ƒH€H€H€H€ì"@ÁÝ.’ÿ¯Ç*G$;pä=/ h¹rå’ßÿ]¯òH€|"±v¹s玴ýµk×J¿~ý¤lÙ²òÖ[o©¬zŸÌnw¼tcÆŒ‘iÓ¦ÉÅ‹wpÍ#<±QµjUõô„'Á%K–”Õ«WKåÊ•=ÖÉ$@$@$@$@$QÜ#JÐÃñÜ=TË]$@$@$@$.)R¤§Ÿ~:\džç 7ªÃÆÇìbïܹ£C¸Á¿=€²ìºqㆼþúëòÊ+¯HŽ9,{B/b©\¹ròæ›oÊñãÇåÙgŸ ]ˆ[H€H€H€H€HÀ&Üm©«±f¸ëm|'   @$€lóŸþY‰¼EŠ‘Œ3:4sïÞ½‚Wœ8q¤yóæòÏ?ÿ¨ò-sæÌ)8&~üøÇ`åêÕ«råÊ•y q3Q¢DrìØ1UueÊ”)Ô1áÙp÷î]Y¶l™²§e žjР\¸pAàÿÒK/™ÕnÞ¼YŽ9¢ÖóæÍ«؇ÊÎ;•·=–7n,… 6ÁƒdéÒ¥ràÀåA~Cä-^¼¸C9½‚ò?üðƒ=zTõÏ?ÿ¼‡í쀕ÊúõëåСC’0aBå‹ö'H@7Åçwxìûí·òøñcúk]§NeGôùçŸËîÝ»•õÍ»ï¾+±c;þ+¾?ýô“êûùóçÕýÔ¨Q#É“'C[Â{_Y+™>}ºÀ*æí·ß¶nv»Ü¥KÕ3fH‡Ü–ã    ˆ(Ç¿’#Zw À %\!  ÷ïßW/P­owd£kË—Ñ£GËâÅ‹U‘GÉ Aƒ”¶>&]ºtá³Fz“šœ5C† jrV½ÆÖX¾|¹?­Û|]Þ²e‹ ½'OžT‡>óÌ3Ѽ[·nJt†¨þã?šÕöíÛWvìØ¡Ö+V¬(K–,‘ZµjÉ®]»Ì2sæÌ‘3gΘYæ(s@<ÇÀ&š9s¦²ÉÁöÏ>ûLp^k@\?xð Ú”-[69wîœjXoÚ´I0GmÅàÀ©S§Ô¶ÁÛÞÿçÎkݤŽëÞ½»LžC€ÿî»ïT± ƒ5ü±yš ¨Aˆõ`Á=_úáÇ«A‘š5kšeÃs_™ ˜x„ j ¡zõêÖ]n—Qm9r¤Êtg–»[TÜA$@$@$@$A!ñG°þ„3ÜŸ°à @`€8úÍ7ß(!b2„id CXmذ¡ÙèE‹)a`ÇQ´hQY³fÊbþôÓO•ð ÑzÛ¶mæ1Èz†·6„úÚµk«í³fÍRëØ¶råJå»mŽ…³gϪº‘EÑõÈh‡  {d»cÝÛ·o 4”*UJeª—)SFeîƒ2»ÑFˆò:aÛÐ~¼#‹þòåË*“?¬L0élïÞ½­§PËMš4‘Ù³g+?ö'N¨,l°Ûºu«|ýõ×fyp*P €jOÚ´iÕv´­téÒæë…^0Ëëˆê›qžÃ‡«¬}´\áU^¿~}ŸðD«V­mFàZCÈß³gºÎÛ§L™¢ú…vâ²Uúôé£î%ˆÿäq_¡Ÿ½zõ²•ðÜWÖ V­Z¥2pïÅ׺Ëí2Fà÷Ž ,0H€H€H€H€HÀ_˜áî/²F½ú6?ž‚U“ €Ï >®[·Î!£úÅ_Tâåøñã• +„¶AA66²Âu`Vd=# b½uRg²aK’$I’èC#üaÞ™_}õU³¾ *(›œß9ó…`gƒ sÕ°_ùꫯL?oˆÍÖs@'Ž1B•}ÿý÷•e &Nõô÷#2ÆG¥,y¦NjÚÇ [ݧ>ùäõ¤‚Ù(ôõnÖ¬™ÊÇuÆÓ°éÁu¿ŠF=²Ô‘a¯ËC Ç î°z¹wïž²ž©V­šºN°#²f•ëã|¹¯t7öï߯18áK Ýô õ¾ñ¥–%    °Pp‹û™áî#0'  ˆt•*UrÛuàs Á™âZp×ûºvíªÍwø™CŒž?¾ÊǤ¬‘ð]OŸ>½KÑ>ôð6G¶·»€÷ü¼yóL{WåàUŽìlx·wìØ1T‘T©R)—_~ùÅôŠ‡Ø ‚üéÓ§Íc´E2¿#ð>‡È&MéÙ³g¨ªnÞ¼©¶Á—ß΀/<˜i;¬£¯xb@{ÆÃSƒxb–3 `?ÚŒ¶¹º?Âs_é,|ܾ,xªƒA$@$@$@$@þ"@ÁÝf²ÿý÷¤Bã  maâÜ0½¶Ρ÷9o×"&fW‚ªsy;Ö‘m?~·U9OÒé\ÙöZñÎubBW»³·‘P¢3Òq>ç,|øÐceÐï%J(6Èt‡Ï;ìhÜExî+œ¡ï=wu;o×çÒø:ïç: ØA€‚»ÝÔáüψ›bÜL$@$@$@‘Jà×_uy>mÕa‘uAdrÃNÅ9t65²Ý#+ ¶CðಉñÇyáI޾Áz™îa²¸á'ÉAá1n xµ{ܵ˜}÷î]3cÜz¼^Ö“Ï6oÞ\ù¥ëíQý¾páBe¿¿w«ï<&ݸq£GÁ=<÷&eEàúûà‹À¸     ðNšj3YZÊØ ”Õ‘ ØN^â˜Ó#áŽÌoL¼éðMGƲ5¾üòKùᇔ§wâĉ­»üºüÚk¯©,oø§;¬dÚ·o¯<Ú÷ù²^¥JLë*à§ŽIfµˆ‹‰Z±ì*áz ÍN[¥è²ð=‡=ü೓%K&Ó§OW-ºœ~‡Í ²É­“Øê}þ|Ǥ­ç¾?~\æÌ™ãñÔṯôÀƒ>¯ÇXvâš!²gÏnÙÊE    °—@Øé:öž/FÕæüOGŒê<;K$@$@$Pà·LdLf‰ áwß}WMüY¶lYåwŽ QOž<)½{÷–’%K†j;ÄÜ|ùòI½zõþåÈf† øóÏ?w(?wî\5É&Ê 0ñ(&EÀϼM›6^e«\üèÞ½»Ê"3fŒš¬µaÆÑé˜LôÊ•+òᇚGB„GßÁàüùóÊú^õ:Яʕ+ëUõÞ«W/5I,|í-Z¤!±½k×.Ù¼y³ò¹Gf:&î„ ›L0ŠìöÖ­[Kùòå£5kÖ({LØŠLÔŠ [±®â>Dyd®·k×Nµç@výÕ«WUÈœG'Ož,M›6êC¦;^<™ðÒK/ɰaäY³fº¸OïÙ1à‚X¼x±âµ|ùråSìƒà‹ôíý÷ßWO7ÔªUK×ë0B¿aÿw{èÐ!™5k–Ú¯x{_éòx/^¼¸Z…¨¯ÏgÝïnYßëàÄ      àî/²F½Ìp÷#\VM$@$@$àŸÚþDX·n]e;¬ O!°" ;,{økë}¼#Câ2ÚƒÁ„7n(ùäÉ“‡:__ý¾Q ²ñ!;Veÿã™3gVþ¡Nbl@æõÞ½{UÙ›7o*Q]g²¯\¹ÒÕ!æ¶ ^—.]R×)}úônŸ@F9Äkd¶Ã¶ÙõÀˆ7®Y_x5j$x9‡@ÐÛk×®í0xí°÷Á ÄÑ£G•ØŽöëðÄÞÛûJ×…w/¾ø¢§"‘ºÖISýyòÁƒ«§0`}ZÁÝ9a „¿Ó»víê®·“ €-(¸Û‚ñI%VÁîO¸p‰H€H€H ø8p@y–£åðöFö8²¨‘‰?~üàë[츯ʔ)cÚõÀŠÇÓßÝ=Ró°Rª^½z@0`#H€H€H€H€¢/ îÑ÷Ú²g$@$@$@$!ð/×V(Ú+åÙ²ež ø™c¢N_“bÒW†g·nÝrð÷\:d/žvpžÔÔ›ã|-c×}_yØíÞ½[àƒï.0Ñ-&œÅ$¯ž„ywÇs; øB€‚»/´¼(Ë w/ ± @PÀ„™ð"·&Å„žvüÑõD–ÞÖ·qãF5§·åcj¹sçÎIkcR[_ÙßΓšúr¼·eíº¯`¿³ÿ~5a¬§scpƒ;,b €¿ Pp÷#afÐø.«&  ð;ü-ƒÌ`ÅÌ™3ýUuŒ¯7_¾|>?=YÐ켯¼ѽ-Y x    èK€i6_[k†»ÍU³:          &@Á݇î~„˪I€H€H€H€H€H€H€H€H€H ÀPp·ù‚0ÃÝf ¬ŽH€H€H€H€H€H€H€H€H€‚„w?^(Ãö”A$@$@$@$@$@$@$@$@$àÀñ£'ä­fí¥O—þrûÖm%¹‹Ÿ'Mµùý÷ß“ oܸ%é3•|²K$@‘Jàòå«ê|Ó§O—åË—Gê¹y2      È#põjÈÿÃû”OFŽ·åÄ—/]Võ¼Ñø-yöÙgm©“•¸&pÕøÿýáÇjçs‰IŸ!½\äVÜý|‘.\¸èç3°z °à—öùóçÃ*Æý$@$@$@$@$@$@ANàÖÍ[‚—qíÊ5;«c]a8uüt%¸››-el¾>ùóå´¹FVG$@$@$@$@$@$@$@$@1ƒÀS±èÑ3®tôí%3Üm¾¶ßia<ó¯ìÛH8ªÍpY øH`ûŽŸäìÙó’>}z©P¡‚G³8 @°ضm›ñÿßY)R¬°d˙͖f¯[½^eË—©¬¤H•Ò–:Y‰k«—¯‘û÷ïÿ'w×”¸5XPp·ùJ=óÌ3Ò£{;›keu$@á!0xè8:l‚Ô©SG&Ožž*x @hÔ¨‘Ü›¶j,m:´¶¥Åe UP‚{×¾]¤L…Ò¶ÔÉJ\(©¨Ü¿ w×e¸•‚…-e‚åJ±$@$@$@$@$@$@$@$@$@$@M€‚{@_6ŽH€H€H€H€H€H€H€H€H€H XPp–+Åv’ 4z¸ôåaãH€H€H€H€H€H€H€H€H ø ¬]õ½ü¸cˎܺyËÜ~`ßÚ÷s ±bÅ’^ƒº æNd@  àèWˆí#        'ð̳ÏÈÄ&‡Ù‹£‡Ž ^ÖÀ¤µÛ­D¸Èh)ÈW‡m#        h@ â+$Mº4áêIÓVÃu"¨ @Á=*¨óœ$@$@$@$@$@$@$@$@$ƒÀ¦Qó†>÷8A‚øR«~MŸã$U(¸Gyž—H€H€H€H€H€H€H€H€b&-^ó¹·µ¾*Ý$,(¸Ë•b;I€H€H€H€H€H€H€H€H ˆ äÌ“SŠ•xÞ§4iI;Ÿ€±p” àå—€         ˜A ©z欙¤T¹1 {mPp6—’!        À&P·Qm‰'ŽWlÜ¢‘<õÔS^•e!ÜåJ°$@$@$@$@$@$@$@$@$Í $N’XjÔ©æU/‡ÃóÝ«ŠYˆüH€‚»á²j         GMZ5rÜàb­L…Ò’)KF{¸‰›÷À¾>l D+_© iÒ¥ñا¦­8YªG@ܰ(¸ì¥aÃH€H€H€H€H€H€H€H€H úˆ+–4jÞÐmÇ$ˆ/µê×t»Ÿ;H Ppä«Ã¶‘ @4$д¥{[™Ú _ˆî F܃ñª±Í$@$@$@$@$@$@$@$@$ÄräÎ!ÅJ<ï²MZÒNÆ%n ܃â2±‘$@$@$@$@$@$@$@$@$½4u!¬gΚIJ•+½:ÊÞÄ(ÜcÔåfgI€H€H€H€H€H€H€H€H 0ÔmT[âĉãИÆ-ÉSO=å°+$L(¸ÓÕb[I€H€H€H€H€H€H€H€H šHœ$±Ô¨SÍ¡7[¼æ°Î6±ƒ­Ál/ €ï,X ÿþû¯:°J•*’*U*ß+‰fGüóÏ?²páBÙ³gœ:uJâÇ/Ù³g—2eÊHÍš5£Yo#¯;{÷î•C‡©6nÜXžyæ™È;9ÏD$@$@AB—}º\¼xQÞÿ}ùúë¯Íý\›žžèÚµ«*˜(Q¢°`   ˆÁ¾Ý¼Brç˃ „týø‘ã2qÌ?ý“ÇâÍ&ídåÒU1®ßѹÃ13­):_QöH€H€H€,–.]*7oÞT[ "kÔ9ëÝrˆ¹OÑË—/+‘þöíÛæv,üñÇ‚Áoí:{ö¬Ù/oŽÑe=´k×.µš?~±“$I"“'O–„ ª2Û¶mSïvþ@ûÏŸ?/×®]sYí;w<¼õtµ³>´ ƒ2îÚæ²ÁÆFp=zô¨Ü»wO'N,2dP/<í.nܸ!àð&ÐÇȺ½iË ØK GîÒ{p{+em$E(¸Gxž–H€H€H€"ƒ€Ö1hÅŠ¥nݺê´Ë–-“«W¯ºlú.]ºHÚ´i%eÊ”’3gN%ÔçË—O¦M›fÓ¬Y3eÅ’7o^¹r効]/@´Å>صôèòTŸ>}ÔzµjÕ”u ¶Ã²%cÆŒJä.^¼¸¸¹7oÞ¬úQÇhawäÈ‘âí€Dýû÷ï«&;vÌe»1Éç¤I“dĈ*Ã"2B·\E­ZµTß`W£ãÏ?ÿTÛÀàÛo¿UO íéÓ§—äÉ“K¡B…7ˆuëÖ ìm’&MjòèÕ«—³ýUê…ŒtL¤›,Y2u½ñž)S&ÁÓz²]ÝÍáÕW_•‡J÷îÝÕý>~÷Ýw2jÔ(³ÏÎ×åû÷ï/yòäQýÄy ,¨ØB´woïEçã¸N$@$@$|Ò¦O|f‹IÀZʸ€ÂM$@$@$@$œ8qB R#ô¤©xŸ={¶²T™7ožtêÔÉ¡«¤Ë•+'û÷ïWÛãÅ‹gøŠÆ–[·nÉ¡C‡äí·ßV–4|ð”-[Ö´«Y²d‰Úg­lùòårøðaµ©|ùòêý¯¿þR™Ðq[¶l)hCüøñÕ>d1ÿôÓO*ëçÇ Ž?üPúöí«DzlC¦>ÚAÛ!f¯_¿^âÆ«qù±Âð‘#Gƒ*UªÈðáÃïO?ýÄ?µU«V¡Ž×m·–³:uê”êDdè'2¿x“µ‚'D}ˆÏ¿ýö›Ô«WOúõë§„hlCv=Þq-ÐoðÑ“ÜÚ]J^zé%ÕÝf´B÷¹sçÔy8 ‹-Ò»EsxöÙg¥}ûö¢utXñè>?~üXoVÂ>&Qݹs§Ú†ã‘úñÚ±c‡²ïÁ@—{QÀ$@$@$@$@$˜áM    ˜5k–iK‚lt|ÛS¤H¡–g̘¡Þ­?àí®Åö>úH‰Ú°þÀ6}D`µïãĉ£‡pî .T›%ŒvkœÆÓ;úÑÉQkÔ¨¡²Í‘ýóêAOu„gÄö÷Þ{O‰Ù°”Aö:|á_¿~}µŒ¾!Ë_ÛÚ`’[WaG}¸ ú#:tè Spmñôƒ¾‹/– .„jy´ s€)Dù¢E‹†*§7àBlÇ“ ,PO% ¯_~ù¥Tزe‹â£Ëûr/êcøN$@$@$@$@QM€‚{T_žŸH€H€H€ü@™ÅÜEŠQÖ.X†ÐüÚk¯aQ~ýõWùå—_Ô²þÿn°ú@67²±ŒÉCa³›²°>Ñ5[·nU¾ãúXØ”@0E@˜GF·sŒ3F ñ±bÅ’lÙ²ÉÀÍ"Ȧ×ìod¿#«â/ìH¹rå’ &HêÔ©ÕúÊ•+Õ{X?`…²zõj)V¬˜Y¢7ìwß}×´ÁùꫯÌýv,.\X>ùäÁS¼€5 ú®V+Úc¿L™2¦¿<„mWv+vÔ‡lu b¼òÊ+‚ëÚ§tà^q0ƒeÐÞ½{•­ î«,Y²8SëÈ`×ãÆØîàž@–;žtÐüâ1ø‚ðå^Tð 'á@cØ    { {o"tv»®YÛË`Ý9Ëâ«XÀræ›o¾‘K—.)«ˆÄxÁס3Ì!ˆC,Õq^{»²gA9xž[™Òð7G@ÌÕÌgDõêÕ%]ºtz³zO üþûïjUowˆLØ×`Àðœ!„ë€% Ú ±ß®€‡¾U`G¦·¶Í:&qµ†î+O´¼u¿õaOÀ?^[ûh«<É ãÑ£GzÑá}ðàÁòÜsÏ9lsµ²qãFA?0€Ó¨Q£PE^ýuµ ç=Â×{QÄ$@$@$@$@$Å(¸GñàéI€H€H€HÀ¬¾Ú‘=®_ãµ¥ ¬`ô$¢hG‰%dÈ!J†È;yòdï¶„Q‡u²UÕ˜ð1þ|õŽÚN"òóÏ?on× ^µW·Þ†wèÖ@6,gZä·îÇ2êA2dÈà¼+ÌudÿÃê‚3¬T0¸Ð°aCó8ˆñôímÉc­K_-¼[÷!ûÛSØU¼çñÔ2ÿÁÂ;&sõf£T©RžšhîÓ !¨ãzapÃúÂ`‹\„¯÷¢>žï$@$@$@$@$•BÌ+£²<7 €­ XÂEÇ矮C½_»vM–-[fNªŠÕaù1wî\e ŸsˆòT/XƒìÚµKÒ§O¯„yd‚câQ”ƒ¯:&&Õ“µºËnÕ7´ØŽÝ¨7¢‰\µUÉo¼á úCFÖ=^ØKdsöyDÛ(ÇãI\k<ìÑ1H+¡.]º¸m*²õqx:C Ò=…¶B_îEOur €{gOŸ“F5š:è5¸‡ÔkTÇa›óÊ…?/H½Ê!V…z߀}¥fÝz•ï$# Pp‘—&  ˆÎ ”kÏïråÊ™çÖ>c¿Î`†­ŒÕfårçέ²ž‘ù ‹‘;vÈèÑ£Õ$§°ªùøã•9ʶnÝZ îXF–;&IE&3²ØµUö…'²fͪ¼¾Ñdb»Š¥K—ªÉ>‘™Ý¾}{WEÌm˜´S[¥ ›»N×ÿH¾‚;âÎ;ê4Ö(ºjÃÿ`Ð` ô×b;öo¿ýVÒ¤Icvƒž<4Oå°~û°ûî»ï$nܸjÝ›¾Ü‹ÞÔÇ2$@$@$@Žðwá‰c'6Λ¹ LÁ}Å’oBwëæm‡z¸B1‘-ebâUgŸI€H€H€¢5íË'N%ªc¢Qç²¶õä£ð×>}ú´bÒ³gO5™hÉ’%MëLnY¡B«³Ì>l2„JùòåÕ:wm'óòË/‡ò\7òrÑ:»ÞÞz AˈúݺuS¡êíîÞÑ/S¦LQ¹^·¾#[_‡öמêÈz×þøºÌùóç¯` ؼè'Ú¶më ¶£úI;ú„ ^ÜW­ZªJLÊZ£F õ "<÷b¨Š¹H€H€H \¶lÜ*ÿ¾äñØe‹žï€5I  àŒ/.§F$@$@$òX&Kõ.œ‹ÜA áSðŪyóæfrÒéÓ§+Á^áð†Glo”ArR[1‰F#EŠd9²T«VÍÜ÷ÍF5ÌD¦ðÔ=ºÀc:I’$f¢V”ñn¾ÆO}-6CäEØ$kE˜“¤I“Ê€T˜ÄtGU½hQ»vm‰?¾š ž"À6B± æ8<´Q7(Y²dÉ$wîÜjÈÜ1,.èk­“—¢@ÇŽeÊ”)>žâ½£>¸ãé€\¹r ¥bñÜORà>Ôáf|z/úx¬H$@$@$ %<% [µt§D,Åø©’{ZNŸPOÞ¹§DúÃû{g=x$'Žœ”wïÜÃýé¶½zG?GSõPŸFþA€‚»Pf$@$@$@$àØtñâŪ'xfëØ+C(b^ÆÛ·oWIDáù 1á\àõ ow$CERÍ:uê(vˆµ–Áç´A€GLuW„ö‰'ªñaLðâÇ—4ˆâðÄ_°`Ã]AP>þ¼üôÓOæJTFœxæ]¯^=9pà€ãÕAãæc™@¼O:%à¸{÷n»±òuÝÀúŽ„¹ðú‡½~ýZÅêǵÆÞË”)£Î=|øPnܰŽíªN8ñ 6gΜ„B|ÿëׯ žˆ€á‰‰­[·ª't“1cÆTO!8{/êú|'  pž@”¨‘¥l%÷ÿ àÁ~óú-»èp2 '”EòÛ-ƒƒÈ3qädÉ“®€dO™Gʬ(•‹W—\iòIŽTyÕ9ˆñ–¶`ö"I7£zíß}@6¬Ù$™“æP¯r…*IŠi¤`#ÜáŠõ–ÕÌmô9vØ£ý<ªŸŠE« ꡹ËÈúUÃÛ™•¹A. À¤©.€È&H€H€H€H 0€gðË—/ŠŽ—mY b:¯Â#"ëãÇU"T$¾„ï™Y~YB2N{†Ð5:|½óˆ+î™uèÐAðÂâbÈ#Á'¼@twÖ'N¬æ‡D°~1Oć‡×<æ#F »MÂ#b?bÞ£¼´õâ&l ÞÛ–\lÏóÛ3CH˰.(çêö&L¨DmÌÇÍÍMyóÃë\?€xëÿ‚;ðJ‡yw Çg&ÔU,~€v¢‹ˆ!†ànÏ|z/Úk‹ÇH€H€H€#P»AMS”^½t­tíÛɪ¢Ûe7¹pÖý˜õªÉë—¯­Îëß?6j'Zœ×Çõûý»÷eh¿rëÆmÿÛ×PvŸ?}–—/Üÿ§]¾p¥à…¶´á««—ܤeý6rêøi4òg}J½7«ÛJ6­ÛluLïœ;}^š×k%c§’F-êÃ|'— àîRœlŒH€H€H€‚„nÉ™3§CBlwxKÃàA®“¨:TÙÉBg=hlJ…’É’%‹àå¨!$À2>Çj$@$@$@$@"£FRIM‹/n&íÕ«Ñ QXp¯Z»²½ÛåkröÔ9«™hõŒY2(1Ûê¤ÅÎöÍ;̽žºIÍzÕ•xŽð}Ï[üÔÌ<“¬C·~×)W¹¬D‹MŘÿ±S+™8sœYwÄ€Qæö©ãgÌí"% K†Ìéå›o¾‘$ÉKÏÝ¥i›ÆR¦B))P8Ÿ\»rÝ,Ë p%z¸»’&Û"   F`éÒ¥*޹žv•*U¤Q£Fz—ï$@$@$@$@A<×çþ6_ÉS³dϬ¶ÏŸ¹`Õ×Ô6ÊxeCÆ ”A£Üý ¥!wΑGÍC#cϪÔ4b¶ÛIÊŠ°7c‡W1æ‘Hõù³çÊC>m†4f3óŒñÇŽKj~_]â'Œ¯„÷Q“G˜ç¹A~E€î~E6¶;jôo'~.#ާõÊf0œ*§D$@$@$à $G­[·®´jÕJÅöFXxÑH€H€H€H€‚.<rKÒäIÔÖ,[§’ÕcG{·ãÿ=Ù^<Ù!´#þúÁ½‡eÞŒòs÷R¿Ê’-y.• Õ«ú8—-gV»Eâ/¿á¥®íæµ[j3s¶LÊ;ïð3ƒz•,ÉrJî´ù¥õ?ae–šñáUþ ? @ÁÝ ×&Oœ<'Ïž½«n7]>ÅW¯ÞÈ… W]ÞnPkÉÚöíûºÂëÙøwï9l$/ûèÙi??þÏ?ÿøyì€H€H hèÓ§ÀËý·ß~$JÅ— @Ð'P«~ 5‰‡÷Ê¡}îÉá×._§Ž!{¼ñ¼œ$´ÄJϘ(›T-YCºýÔS¦O˜!¡f¢F*ˆ±îʼnÛÓ" Å7ÏÝ3°Âð¿èÌÅÓ¥ï^'î׺H̺ÚðÔïØ²‹dO™G~8Ã¬Ë p5~#r5Ñ`Ü^“Ƶ¤EóºR°@N—Ïr̸™R¼T}—·”Ä¢ò›ÊŒYK½öŒ™K¤Jµ–òåËo˺²ÀÅ‹nÒ£×ɘ¥Œ+›e[$@$@$@$@$@$@$@Œ@­ïÝw kÕ’Õ‚Ð-wnÝU£DHï¬g‡>‚¤¦oß¼5t„ 2rÒpÙ¸g\¼VžÛ+HÎê=yüÌÓ"Ï §Pm±bÇÔ›.\8éÔ«ƒœ¿{ZvÿSõ[Ó˜‹.óþÝ{éßm ¬_µÁ¬Ã p%Æpw%Í`ÞVùrÅ/¿0<æïùÿý7Ä>†>zÌ ÙµûÜ·Ê[ÄmZ5b%ëÉLCœÿ±MCoËû¦ÀË—¯eÉÒõ2wþ*9~ü¬j*räH¾i’uI€H€H€H€H€H€H€9TiSI¶\Yå´‘ˆôÕ%tw‚vå½=œ W,r×7&N(›ö­—"XÕqĉðôñÓVu,wN;eî&K‘Tm?zðHÞ‚:,qÒDÊ‹žôÍ~l"ˆ*¡ý÷ésÕù Æœ#žF®&@ÁÝÕDý¡½?ÉÊU›ïyrg•lÙ2È­[÷äø‰³‚ìÍÙ²fäÉ{Éž=GäÊÕêxút)¥pá<‚$‡’»Û_¤níJ’5kz³.ðå+6Êë×oÕ±0Æ/×ïëU1V Úeô„Ùõl—ÏŸÿ’R% J”(‘ ¡ö¹pñª¤4~ñÁ;>ºñÈÚ–­{äÎ’ A‰#º,\¼ÖH˜C:´k"=5Dä•òÉHÌѤQ-É‘#“ÕÐ>þlà‡åêÕ›âfÄ ê[É‘=“4¨_UqÖ…}z=t}¼c ðòÏœ9äË—Ýò”Ýí"Eò¨ë>bätiÞ¬ŽŠ‰f· "¾ÚÖm{eî¼UêšâH€H€H€H€H€H€H d€—;÷W/_©ì˜}¹Êe$’7ŽxáOrXšt©=ˆí8~hßa¼yiëVþ!=~î*ÿ-íÀžƒrÆÐÁ`)S§ˆú°i~“éãSÛÝûw1êvSÛøxòm:¶2w=>³7HÀE(¸»¤6séÒ5iÕ¦·¶kÖ(/Ñ¢E‘Ù¿/³B£jÈÄñ?KÔ¨QÌã}ú6Äõ“j¿h‘¼²rù4©Tµ¹9òuµpᢵrûÆ~ÓËüéÓçÒ¬E%îë† ègÏî1ÎփǥeëÞ*ÌIÉåÜù+òäÉ3 &Œö'O'§Ol”1¢©¦ ö׬ÝVn#=|øDËœ­œîF½gÍ’^-˜`uÌ™É5C,G¢¬®ââñÎ]‡äܹËFj¼³f/“[×÷Iœ8±Ìæsä®,` ÃÆÝ»ÝˬwïXbƨõéõ0;26æ/X-ÏŸ¿”Ží›Zör‹#½ûŽ’ßç¬6­xYÖÑ“ƒEÜX ‘ „\ÕëV3Rš tX-ÂÉ$J’P ÜÐ`î=$«—­U^ñЈîݹ'#Œ’“G¿z¨?3ô#{†>«•ª%g—BÅ (gÑ[w©Xìº|ûî?™:V#‘ªÜÎ&KöÌR H~‰bècO?•±ÃÆëjR¤das›$àJŒáîJšþÔ<±ß¿¹(1cF—U«7«×„q?ËÑCke˦yR»V%àÖ®ÛÎjDö®”ï.)êGÆ/™‚EjIذßÉúµ3åá½#2göhéÓ«­ùK •!@¿~qVÞ¾:/³fübÕžíN¥Š%åùwAÇÎR«fy¹ve·j»{·VrïÞC7a¶Y âw¦Li$¿áÑ?~u<¾R ÿ×W®œ™Íò>Ù¸zi§ ö<SZ²h’1—3Ƹ*ÈþýÇ$eʤâvy—,_:Ey˜¯[¿Ýª‹zu*Ëü¹cåñƒcrýêõÞ¬iUwõš-fYŸ^³ccÚ¯ Ô.®£V¹RIUtælïc¾{Õ&žL˜6}äÉ_ÍxÔª¬Œ;“b»WÀxŽH€H€H€H€H€H€B$-Vªˆ9Û1cH‰2ÅÌ}Ï6àM^÷‡Úê4"´nØV’EK-)c¥SIK—/\)‘£D6«÷êØW~ll­cádž¹åñ£'R¯RI=¤2ê7¯×JÞ½}§ê–,[\ê4¬e¶S¶R3L ÂÿP£©¤Ž“ARÅNo$oÍ*ˬPe³çÎ&õÕ1ëqƒ\I€ém!ëò7ß|£B›ìضÈÊã¼LéÂF8—ÞÊë}ÅÊMJ€×C 6¬ò̾rå†Ô©]Q Êø%kÜÈ~ ãxÙ #£ÛÕï(ƒG8<ìâÖ¿o{™8i®œ>}QíãÆ?|hwµß¹ë¹páªÌøu¸•àoöÅƀŠ-fã}åªMÒ°~5Iš4¡!öÇV­ß7â|YZÿ~íÕ.~A_¿~[‡'íÚ62<Ê—Ë™³—”p¯Ëûôz >·\¸à¦Âí¤M›B7éí{† ©%V¬rùòu§cß3dŒ·xY€H€H€H€H€H€H€HÀ €©;·íV,ªÕ©¢"8æ—IÃTÜ÷…³«ÈðvÇ áhºôî(M¸êe T«—Ü”ý›Wo<4[½NUiÞ¶© è1XŸ][Ĉ¤~ÓïeȘ¦ö„sК¦Î$2g™Sf «ÂKþõ«×ªjt#ÜpÝjIÏݽ ‹£ûâ; 8K€‚»³ÄYùâÅò[‰ízxÝ»¶T‚ûž½GL¡YŸÃ;â´/^8Ñ ‹byÎÛõêV¶ú…)RDÕç #æW@[ĈáÕ†ˆòÖµá—ñxÃÊ´ùrûö}}Xt²Ð—/=þ@!Ÿ\ʇž0a<³G7›þìÙËrÿþ#I”(¾·Õ\2æÃ‡R¦\#oû è7Œ˜q°õë׋››[@‡ý“ ø³gÝãyÏšú»lZ÷õ©tßtw±7Zt÷ð¸¾iϲî›ÿråY ¨íTiRÊÓ¿xÚ}­ú5/ÏlìôQ‚—­Ao5y„ôØ].a‡ß½{§â­#Á©vØÜ~d‹ -!{®l¶M¨ýFX›jµ«È¹SçåÞÝ{’$YCPOo¥;YVDR×®};IûîFc#Ìð]#„Ò]%4éS»<žeßÜ& àÄïím; âªCH¦jÏ¢±«à•íWö“ËÖB‡¥<±mû÷>ÁÂUV@-­{ÏJp/W¶¨J°Šä´ððG\zÄ´÷Ì|r=Ö࿰:žµmïxüxî‚;’ºz&¸c¾ˆñ?cÖR9~Üý{m9s ¨¶ïØïL•-{ïÞ=#¤‘ýÏB€Œ“ ¸”€Û•k‚—+íÌI×|—v嘂R[CS°h~»C>¼q®€Ýs–¡aeÍ™E½,{µ Á?m†4êåU9ž#W àîj¢þÜÞé3—ìöxòäu#@ÁÝgÜM­sç.+OìΚ›czñâ•tî6DíW­RÚ<˜7¢þ—(±Ò³dIo±½öî;j$VÍáoBüÛ·ï qú“ÝXòƒ†L2ÇfoÃ'×#Mšäª)$²uÖî߬ª¤L™Ä˪ˆaß¾]cõÂçÎ_%‹¯“'žd÷²1ã$V–±øØíñãg‚W̘1'öár|$@$@$@$@$@$@>$pëÖ-yýúµÄOObùÎ\a× Oy8Ç!JÄÿœå\Ñ.Ú¸|áŠrŠsU{l‡H ð àx®…F’8qéÙ{¤¬]¿M‰Ò/_¾–uë·+!k±¢ùT»¯ŒÄË–oOŸ>˃Uœ«‰“æ˜}fÈJJ—*lîcÞæeñ;qòœz_²l½Á±ƒxè~¨¡â_AèG°#GOËž=G¤hѼjÑâµòðáRfþ‚ÕÒ°A5«6Hô:hÈDiظ‹´jQOñçÿܾ_0§?ÖÍ’ŠJ¨¶œù±íÏ}ráâU% #¬JÝ:•L!}ý†í’*UR‰kd܆:uQNžÓÅϤz:S'¤”c„ÏÍ•/§š.¶i$ÔPpjWÌf¼%Šç—vmÉ€A”üîÝɘ1µL×_¸TÛ©Ó¤C§AV¢jç®î^ð(S¸pn‚»›Û-éØyéu;x3v¦¹!Bx)Y¢ $OžX‰Òº¸·¹e룯/Jpóæ­êÂ9¬K·¡R¶LaSèÆ±¼y³É²%“þ«²8†˜éˆ¡Þ´I-‰íh£k÷aráÂUlÊOíHF#vWÒ$ UÛ˜GÞ<Ù¤Fõr†ÀžQvî:¨’¤þ>k”,^0Aê5è ˜BXG2ŒeòÄÒ¾ã@C Þ¡Äù\93K† ©Uûøáèõ0+ü·ñhsßþc*ñªíy{û»vR+íå˳wÚÛc˜SåJ¥Ôëùó—²xÉzÃó}¥±ðÀ3ÞÂc       ?!P¥f%Á‹FA•÷ zå,ÆÓ}mc[œV›Å‹å—Ï®Øör?kÖôòá­ýñö*–-SÄnQŒp1ÏŸœ²WÅêXÍ寧OŸ^õŸ$a¸{{[5d±sî´ýÌä¶ó:|`E-‘4iRȉ£ÈwäÍ›w†¨žÊôd_¿ö낃U¥ÿv¹¶õzvo­wxšãZ9b+WmVźX„r¤ž½2®9c¯#        àNàÛà>AÎ/èˆ;¦$MšÐ×b»ogŽd£)S&•ìÙ3šb»oÛô¬~Á‚¹Ô“áóùógÏŠ™ÇÛ~ÍÚm’+W)T(·yÜ:ä̽ÛeÝš*áj˜0a\Ñ4Û         `M€‚{¼¼dW®Ú¤B½\u»)3g-U¯cÇÎÁÙý!»êz ÐIy÷/]æß+2 ­ÄÌ3²WÅ|uN‡œYµbº<¸{X&Ž |Õ(+“ @0&À2Aðâž={YêÕï ÿüó:tR½0 $ݲi^œ‘cCFBÓÇOž9Vø¿R… Ïq$õKsÕõ(P §ŒÓOV­Þ"ÕôrÈ«×l•ñcûK‘"y¼,窓¶!gV >4       ßèßm DŽâ·º…ïFÈÚ~MàØá~ÝÛ÷gÜý¸+ºËm$Ø|ûê¼ ¬ˆ6„?ñkaY÷PΓM›w;ÕýÎ?IdÓ8UÇÙ®¼;4•v?5òvõ*T(oËùE„œÁ‹F$@$@$@$@$@$@$à31bÅçÏ^È®?wû¬Ö vb!–iÁƒ÷ zÇ'x…$›3{t ®+¯‡#Bº#e-,ŒH€H€H€H€H€H€B8«çÊÞûäßÿ á$8}€Ø^±ZyÂ&(¸“ Éi  )S§¼h$@Á“¦¿kÊ‘  î]’ ?܃ß5åŒH€H€H€H€H€H€H€H€H€H€€÷€Î.I€H€H€H€H€H€H€H€H€H€‚ îÁïšrF$@$@$@$@$@$@$@$@$@$@@€‚{@g—$@$@$@$@$@$@$@$@$@$@Á÷àwM9#          @Á= ³K          àG€‚{𻦜 @ àÐÙ% @ð#@Á=ø]SΈH€H€H€H€H€H€H€H€H€H Ppèì’H€H€H€H€H€H€H€H€H€H ø àü®)gD$@$@$@$@$@$@$@$@$@$B@Ÿì’H€H€H€H€H€H€H€H€‚j¥j‰qr$@ƒ=ÜÇuà(H€H€H€H€H€H€H€H€|…ò¢Ñp($x$H”@'KxÈFB÷@vA8        €'0vÚ(é;¤—üóÏ¿?Ž D8uì´Ô¯úƒ9×)¿O”’åJ˜ûe#Zô¨:4eeÏ®ÉxF†ÇI€H€H€H€H€H€H€H€B41c„èùsòþK j´(VFŽIbÅŽiuŒ;ŸCÊþkÄ’ ܃ÀEâI€H€H€H€H€H€H€H€H€H€? îÿq„$@$@$@$@$@$@$@$@$@$@A€÷ p‘8D          ÀO€‚{à¿F! @ @Á=\$‘H€H€H€H€H€H€H€H€H€H ð àø¯GH$@$@$@$@$@$@$@$@$@$Pp‰C$         ü(¸þkÄ’ ¡ƒÀ9D   J`Û¶mòäÉ»³ÿî»ï$}úô’.]: &ŒÝ2<°ž?.GŽ‘cÇŽÉ©S§$A‚’'OÉ;·d̘1`Ä{_ºt©|ùòEÍ¢L™2'Nœ >#× ? Ùœ8qB.]º¤&S·n]þnrÝeeK$@$@$@$dPp2—Š%  G`èС²oß>/'±½bÅŠ2kÖ,‰3¦—eyÒqË—/—W¯^I¼xñ¤J•*ŽWü¯ä¼yó¤mÛ¶òáëºÓ§OWûÕ«W—ßÿ]¢E‹fuÞ§;ׯ_—;v¨ê¸&LèÓ¦|]ϯÇrôèQùþûïÍq8P `î‡ä ÿ`ãÕgcÑ¢E2~üxu ð¹áb`H¾9w   J€!eBê•ç¼I€H€H ˆ€p¯vý *”šÁßÿ-k×®•lÙ²yê Ħ(†ûóÏ?KëÖ­eÔ¨QN×£Y³fÒ¤ISlOž<¹Ô«WO ( aÆUí­Y³FräÈ!§OŸvª}Ï Ã‹ãÅK{{VÖ¯ûõX°PaisæÌ‘þùÇòPˆÝö6>ýl„؋‰“ @#@Á=„]pN—H€H€‚*›7oÊçÏŸÍ×_ý¥„Õ’%Kª)Ý»wOÆŒT§lÆ=yòd Kš4©œ?^nܸ!K–,‘(¯ùV­Z©ó¸¦ðÔÖ¡QÔAþð’žKXªT©ÔûíÛ·Mï~u „þ lð¤ÁÝ»wÕ+räÈ!ôJpÚ$@$@$@$² PpÙן³'  KàÛo¿UñÛ¯ù›o¾QóX¹r¥§óù÷ß•æ¬'0„ý«W¯ÊÇ=mÛ³¯_¿V³gç}{ܧsB¿X ° ÷âÛñ fû!CT3ñãÇ—ƒzˆÕ.\8ùí·ßT¸¼|ù²è03¾íßÙú`ü d«V­’7oÞ¨!Ïœ9S´¨këÙmoNŸ>}’[·nÉýû÷ýÜ#Þ™û ÷ñ;wÌyÙ»#Ç|ÃF·ÿöí[5gOèúQ£F•D‰©—þ½¤ÏY¾ãZ@˜ÇÜ5GÙúçõvv,O$@$@$@Á™÷à|u97  bÅŠ¥Ä-LÕž€…$†¥K—V±Â“$I¢â¼#Æ7Ä`[ëÝ»·¤M›V*W®¬„Ønݺ)AǶlÙb[Üî>Ü~ýú©Å€èÑ£KŒ1$sæÌ‚¶!ÞkëÑ£‡ê mCt¶5xã^¶žû>™S¹råäÿûŸtïÞ]yž'NœX"EФ˜îß¿ßì¾jÕªªOô;yò¤9µ^Ùèѣ͆öíÛ«$©ž•‡'°/ƒäZøFzÞö„x\}ãÞ¾}»ÚïÒ¥‹ÙUÓ¦MÕ±^½z©c¿üò‹ÚÏŸ?¿zBánï X#‘ë²eËÌúzCß2dЇ¬Þ+Uª¤ÚErL˜#c±jÀ;ZXO™2¥+VLªU«¦ZAˆž/^ØmqÓ¦MR¢D ‰1¢ ¼al7jÔH=} +Õ¯__ÍɈ±xbk/_¾T‰ŠÁ÷L3rôþ²lsÏž=jˆã§!´X=bÄy÷îeQ‡¶} c1­S§NjîÆ>|x©P¡‚Õï G>ú^#Û9à4hZ„Âg¿0÷¢E‹ÊîÝ»=Ìѧl½Þ:ä    — àîŒl„H€H€H  @Ä~üø±êb™¥W)B›,XP ¡ð Ž%Š„µ µpáB«a?zôHy³Ã ¸M›62vìX+‘ܪ°gÏž)ñlذaråÊ•01çVB„-d.\Xõïy$a´5¨8‡„Um>“›››X!Þcœ0x×?~\Ê–-+Hô ÃÜѧ^€©Ç¡EqUÐÎÇ«£¸:lŒbêPœ8q¤FjB1ÆCxÝŸf¥Nü÷Èúüû÷ïEï?|øÐ,`”Áõ„=yòDí#¶;SÐâ1Dg„)BÌuĘïÙ³§Ù6ôý€¶ì%pÞÙ0GÆb¯GáA¤†é¤©úóX¼x±‡¦æÏŸ¯æ¼k×.åÕŽ*\x?/X°@Š)¢®9**THÍ @öžA®œÃœQ¦9z©JÆä@8(ÌŸMí©—>}ú|ŒÑQó ´p<¹sç–‰'ªy…ZðîÿÍ›7 >§ëÖ­SÃp䳡ï50²ô’§|ùò —.^¼¨¿pÿaî{÷îU "X¨³4Ÿ°uæz[öÅm    × àî:–l‰H€H€HÀŸ @вô‡§²6pÍ!DÂ3ÞïñѬT©RêxãÆ•0®ëèw”…°ÙðÚ† ž={v}ÚÓwxT:tHyê"Ô <\!¨Aà!‚Ö:vì¨êCP„ø ³'nBp‡ÁS6W®\jÛ7sBõmÛ¶)Â0Úúá‡T»-3eʵ½cÇyðàÀƒ‡\†@IDAT1ûxÁ#Ü+ƒ‹/žò ÷ª,Îe̘Ñ,¢ëšÜ(_¾¼Û´iÓÌðVÇx'L˜`î?æêW¯^©kƒ'àõ ƒlïÉuÒÎŒÅæ<™;w®‚Þè0,âèûhöìÙê@HÆç$Mš4J\~úô©š;žÞ€AàFÄ{ýÔ=ñ^?;vl%ˆ«JÿýpôþBñ3gÎ>+xâ‰t±‚Ï ©p†xÿX¤rÔ|ÂmãɈ .„öI“& q°´~ýzõt Ø5Žûæ³ÞxZ†§ ¦cÎ8–5kVu]±P‡…[s†­3×Û¶î“ ¸†w×pd+Nx÷î½áMwV6lÜa|©=a|ÙxëDm% ©ºví*Í›77_Á "Ž7ÎDÏXmZˆG…£C¸ÄzŽ7®"!¾Ú„Y„Õ@èeµk×–dÉ’Ù³Ú‡»N ¡aF„ #ðpGè„PAˆ‡Øsðª†;wÎJø‡Ç¾~4h Êà‡oæ„úðn‡Ðø÷)R¤Ÿþ‡•Aô„A¼Eüu0Œûx… J³÷"%Ä\;b–åì…Õq¤ x#clߣ a|p á:l ‹0{ñ´8 Ì "ôÜl[Åá}gÇâpÃFA¿•aÙ²eS÷'¶qpÂNŸ>-§NRÛúǵk×Ô&®«^0Á܇®“ðŽG00Ô!jöíÛ§âü«Æ<Áa÷¯­9r¡Nß¾}•À ¯ö+V¨ðK8ŽÏ*Do|6a½1Ÿ²ÁÓ«W¯V]`! a0/„“Á“ø¼Á° …¹ûô³Q}Ñ¢Eª-„3ÂÂæˆû yÙqM`ø=gÏeëÌõ¶×‘ øž€û7)ß·ÃHÀ!ÃGL•_Fýjxü½7ËgÊ”VΞÚlî‡ôW¯ÞÞf ¯¿4!çO$@V´w­ÕÁÿv V!'BbhCÐý E0â7n´‚Ç9DnËðPV…lv|ÊFÍa1ÏÖ×]Çç··xc[Þ³}Ë{J?ábY X”Ãuâ æŽ'b,ÍQ¶Î^oË>¸M$@$à?ð7öù³þÓ{ r^š¥½}óNž=õ˜[DzLÌX1þ¿É²·ýŽw¿cË–mlݶWúý<Öˆ¥›KZ6¯')S$‘{†°%J$›’!{w̸™2cæyòðxÈÁÙ“ € $„±6x^§K—Nyç/^Üô`ÇyÄóFŒn€/îðtÇ=P0¸çõg QH˜Š§*«/’vâx»víTXuÐøpKð„GX¥%K–˜‚»^ðB 9rèâæ»£÷B©€1̳kûÔÞ½jvf³áS6àÓO|Ø4«˜ê§lÏ9³¯ŸA?–a”,ÛÀï®%žö°dì([Ôwöz£H€HÀ T)QCï?⿲· K ]3÷p”^M _¡¼òÇ.÷p”^•ã9ÿ#@ÁÝÿX‡øžvî:¨LÛ_rætt9ÄC±àýûÆcÛ/Ô.G=»ì4ÃC$@$ì $C„ š}jƒ·'£Ù¾Ct·ô(…0æh_º-Ý'„Ox{e:dÊÀ˽ÿþʳb5„^ºÞÈÚtûØ÷Éœt;~ùÑs@lj„6ñ.îýîÝ»Íá „_þ®B¼´gZ Æ9,À&ÃN܉qáÉ Ï MX°ÑÉT“$I¢b¤#’ïß¿_%yE~ŽÃÀ]{5ë±àšâÉ[±Þ7cqfXlÂ=+\¸°ãܲ œ×ìVF î(ƒ9!4‘O„Ð4Èc¡Éc $u¢pÜïÜaðrG’T=݆:éƒx ãÁµ½yÓýÿAÛf06<%E0ÄÜ÷Ê|ÃFfÚù0OKCâ]„Õ•,YR%7µ<ïè6žÄá $@µçÙo™Ã@—w´}ÛrÎ^oÛúÜ' 𗜓X±½~*ÒFÂ^‚*„šIŸ€­ñúQpŒWÅc9ú7Y¹j“ª‰/DMPqÀuSñãÇ‘™¿ å‹ëCêIK›¶è¡ððáÃÄ'•¹óŒ²ýÇHÓ&µå×iCÕ—"]©OÿÑrèÐIµ[´H^Y¹|šTªÚ\Ž9­‹ÈÂEkåöýJ¨À‹DIó˧OŸÍóé2–2·±±fÕoRµJi«cÎî8:Œ§U›>Æ£âÏTð2š6eˆ´jù½œ9sÉ8×[‰ä8‰sÓ§•–-ÜÚ9Úê8x\Z¶î­¾X•,QPο¢úÔ_0'O'§Ol”1Ü“¹áKgÍÚmåæ­»FÈ€'hB2g+§Þõ¬YÒË¢ô.ßI€H€¼ a1ª‘¬pÆ 2lØ0+U»wï®bE#d‹eÌkÏšÕâ«gç³fu_¨†8‹>kÕªeU±™uRWx´ë5HÆŠØóHR‰’ˆ+[¶¬JÒ¨ñ‹9é¶½z×±ä½*£ÏA¨¹Þ–cá6 €ï ئÑ÷í²&°|é™8~€êµE«^’=[FÙ´aŽ;¼N Ê_¾üO*Uin‡RûxCÇ×<†úX ‚yö”‡:éÄ,8Á+÷¹—tÿþ}éÒ¥‹ q^',Ŷ=ó ´A_‹îÓ®]»T7X¨ÀgU‡îÁ}`)šëëëèg£X±b*6>Ççíê{ÉŠqÿa¡¦Ÿ,P;NþðÍõv²+'   ð‚w/àµS«Q½œ@/W¶¨Š•Þ¦uÙ¿g¹ñh{Xiݶ¯9­ƒ'_ _ɰ!]¥ZÕ2wXòä‰eʤƺ(2Þ…ÅÒà]…/ÔW®ÜlY3ȶÍó•À7nliܨ¦ ?KËoˆÆ±Ó¤I®ÛûxU®TJ˲¼³Û>™G·®-U7sæ­°êÞåk×m“Ö­ê«ùë“>é": <'ŽÿÙHz—Dy´÷ïÛ^yr>ýõK/„‰áC»ËŒ_G¨4¨7ã×ájÇðêÜ©9ÓH€H€$€ð&µk×V¥!°G]råÊ¥Þ;tè Ž#ÙªÞv°YO‹áo#ÄÜ(Q¢(ohô•*U*I›6­!vâI'ƶáf0NCÂtäȑŞëŸsÒûEcÅŠeüŸ^yM{ à¿+u *ÂK;ƒÀ{·Q£FJxGñüÑôìµlW{#Æ6ê"ôÄÄråÊ!ïr[5·‘ô×Ö¯_?5ÞæÍ=þíDHGø,|`œÛ·oWõð¤­q×â8 žÔ؆ð oxhë…UÀ⇣c±¨âé¦åâ€wá\,ÇŽûLýñ_8%„Mg98(P@Í_'ç…ç»í=‰'4´°ŒÁឬV­š§ãtæD5Ì'@ðT>›¸ß°øzÊx7_ß°Áb î—iÓ¦©Ï,î5$‘Œ‹x @[ci>ùlàwîˆù-[¶TIaõýq†û­^=÷'+-ûstOfèðYÎ^oGû`9    ï Pp÷žQ+ÑÅŽ0›:ur%ª_ºtM%äĤŽ;£™FìöŸÚÿlõêÓoŒ‘´Í=–Ø©Óì2@œöÅ 'Z=l· ôÉ<ŠËo|ÉÎ(ó¬¶ wƒP/ørÚ¡]«Qû¤Ý@½º•ÍGÓq,R¤ˆ*Æý‹—¯t¾“ ø$Š„ØŽ° à  B"BÊÀ›ÜVhôÍP&æÌ™3*Þ3Ä/xÀÂC†DªðІW­­!Dˆ¥À±·=ó¯9!‰fÑ¢EÕß0çH,ë¨A‡ Ï]ˆ©0í Ži–Ë–-Sb§^h°lû§Ÿ~’ž={ª'ÀpÜÍÍMÅØ†—vïÞ½-‹šÛ¿'L˜`ö‡ñj/b³±kŽ… $ÛÄ!€b ðÞþóÏ?Í>Q‹HV ¡†6‘  »wï6C­¨“?‹E»›è¡N`3‚§(¼2Œ+_¾|ªžªÀBBûöíeæÌ™ÆÂ u±ÑáÙŽ°6ðÜž>}ºÀ{ßÖ"FŒh†'Á9ð–‰…mË;»¡}âĉJxÆ"¼øqà¾Ç‚ ž„ðÊ\Áíã3‹û‹oøÌ‚„Å\gÛEŸ|6°ðvþüyb ÷’Û¢_< ‚„šÁgÛ·æÓëíÛ~YŸH€H€H€Hà+oŒ/"Ÿµýz>Øláqeüc?eÒ iû£ûcÛÁfrÿMdÚôÒ®ÃAHxUÛZ·ÃeÜøYrôÐZÃÃ/‹Ä‰ŸË>H;ÉQu]„VAsæD/T´¶„1¾0ïÚáî…¤{õÞ½çp;n–¼xzÚÊ{Ü«:Žœóé</Y' u–¹¿‘F?ÔP,oø8fi>éÉW#FÉ úw”?w´lN…âÁGïà¾UVDZӹë™8iŽüï¯ëJü÷P€&€'™$mÛ¶•©S§:\I€‚'»wïÊ¥K—”g/<¦!(ú¥!L6xÎÂÓÝU¡k,Çìs‚ øìÙ3åáìSf×®]Sâ"âë׳Å˹aá6 ¶c‘ öÄyÛ:ØG˜„Š3¦òbF˜„ò€¡M<…€7Ïmx­[ÆW-~@¾eÄDGH\KxA;j¶cA=0Å"PéÒ¥UòRGÛòM9,,`”ñ?îGË9öÚFX„¦íÙ³GÅu·Wηǰ8€Øóˆß19zø¶_ÛúøÌâžÀý†q$L˜Ð¶ˆÕ¾O?¨wåÊ#oÏCµ˜£Ÿ¢°jÜ—;>¹Þ¾ì’ÕI€H À hÝgä¤áÒìÇ&>Ï;LuŠIS=#Ä㎰LšúôïŽV 4å®\¼*…²SßÏðTm0±†#oD÷˜ÁdFœ†;x¤ÛÜOž<¯ ¤NL½§M›Âˆéy^þܺÀʛ˽• óÓ§ó¨S»¢ŠQ?cÖ%¸ÿ>g…ñ%ü­tíÜÂÃä}Ú‡‡†8 µ}øðу"8PƒEH€H€!8qbÁË¿ ¢¡­g¬«ûö9Á#B¨o "5^μºšÇYó,©¨e;˜“£óBèxˆk/qËv¼Û¶7x•c!‰qýËðÿ<àñrÄà¯CΤK—ÎÏÄvŒâ¿_,H92OË2øÌêı–Ç=ÛöégõðÔ„~r³ö}sÜÙë훾X—H€H€H€HÀšCÊXó{½ûŽ–ÇFBSK›7•ìÞsXŠÉk<’E*Sª°‘¤ê“LÿÕ:.¥®wÿþ#•8Âo`6ŸÎ_v:¶ob$cC2²K2qò)Sº°dΜÎÃt}Ú‡‡†85JdUêúõÛV¥‘ÜlûŽýÊßêwH€H€H€‚ 7 ’ê¶nÝZÅ’l5j” (B¬À;†Ä·4   ðŠÀå WäÇÆí¤Dî2R*o9iñ}k¯Š«s+­’| ©¶ýÓðÄÙÌ)³¥jÉR8[q©R¢ºlÛäž×ãðÉ|üsüì+p ‡{à¾>>{ιŒT¯VVÅa?vì¬ìØyÀˆiUfÍøÅl³gÖ²rõféÔe°,_¹Qjå3gN+‡Ÿ–={(Þ1òç4¾¥0¾t½‘eË7¨˜ç}úøE³l“H€H€H ¸võº9»þÃúH¹Êþ—§Æì؉Ï_ÈË/UÌÙ2 íZZP›åع8Pp×!ÀGaßÀ'ó€÷º3!Z|Ò‡Oç;vLŸV ðzoß¾“å+6Ê\#@ã ‹æÍêø˜8      NïíÒ¹Ë%ZI–"©ÃO¦½{÷^®qד§J&1bÆp ÉógÏå "*$Нú|ùÜ]¼F#ñÄwª-ÛÂÿþû¯\½ä&oé2¥S‘lËøvÿ…ÕxãyhÎÙùܸvS>¼ÿ`„wNo2ØCÃ6àœxåâU%þç+”—ŽŠ6|‚ò.÷ |õ,Æ~þü9|ÄÝszÉÒõ3ft†¤fr!Bx‹’Ü$¿#€?Œ;w”¹óVÉê5[TR^ôÖø‡~×)[&      @ OºòÊQ›=WV™½l¦´ùá'Ùµm·@t‡EUŠ—.*£¦Œè1¢Û%²~Õ#vùX%ôâ;<,uºTRëûšÒ¹w‚}š¸U™²•J«vûu f/Ryé&N¨„æw†Ã¶êeÜóôU¯[Õƒç¸.ãÙûÖ Û¤o—ŸåöÍ;ªÂÚalÝûw•jµ«x¨¦Ç–,eRÙvp“‡ó“GO•Éc¦©ãã~-ь܆Íê¶ÝÚvüt;é ÿÒùËâÈ|à!ß«c?Ù¹u—qMÜÍGŠIòäÏ%CÆ „Ö±4˱lؽV=|$]Út7çzåÑy§>,Ûçvà"@Á=p]¦oÿ1òdžª~Ÿ~£Õ;~1¥HžX Ìåãvý³âÉ“çåñ“gNuYȘ[dãZH³ÀÆêÚµ[2oÁ*™¿`ܽû ¤]ΗH€H€H€H€H€H€üœÄv½÷ï=ÚåëÉñÃ'¬úDâѵ+Öˉ£'eñº’.cZó<ÄuˆÙ3§Ì6é ·Ë×dÄ€‘röÔY™6o²á¸AŸ2C¯@„nÕ ­lùc«yîoCèסYôAx¦ÃàñíŒmùc›ìٱתŠövoY¿œ:~ZüÙê¼î;†átjÏ>~üdŽïóçÏ*'¡®£Ë#÷ >†9êm}ÞÞ|Ξ:'k5“{wîëbêõ!à+TIf-ùUJ”)nž·ËáG¤·!Öë…³7‚  îÁäR®]=CÓÒ*%|øp–‡õöä©ódÓæÝNq矋$cÆ4NÕ …+Ë1– wƒ_ÎH€H€H€H€H€H€+„!5jÑPZwh)‰“%’3'ÎÊ ÞC•÷ö=éÚ¶‡lܳΜÂÊÅ«M±=ἆx=@2dI/·®ß–á?”Më6ËÆµ›åçîƒdÌÔ‘f=½qüÈIyôà‘À‹»zª’*MJ‰hlgË‘E†õÿEvý¹[6w²¤I—ZbÄr.L Äö¸ñâȘi#áU د[¹Aõ¢<Ò§ƒ€]LŠ–,¢‡äô{®¼9eûá-rïî=iR»…ª¾†é°0aÃÈߟÿör>è[O@l>¼ôÖ[ªÞ÷áÂ…•m·^ï}åÍë7Ò°Z9vå à)[=dœÛsÞðÅJäb „ô;Ûùpß# î™É#ðf5J»ôœÙîžùzŸïž(Vž…Œñ|¤Äö¬9³˜ûŽn„ JÖïZc¶-z4ù±S+#ÌJti׬£jfÄ€Q¾Ü#G‰¬Æ1rDsXQþ;f06¼šÏìisäÚ•kª8ê4¬eV­Ý ¦ÄŽËxúà{åM?eìt1a¨y^o€eÇží¥ï^Bøè2|Ú¾ ÚÃçèI€üƒBÆô0V’¥,,¥Ëþ ‹¯5ã³ûGÿìƒH€H€H€H€H€H€Hà+N†`kkÓ¡{;óðÖ ªíCûŽ(ïtì´hÛÌÛuA8q¶éØJí"Ìɂًõ)«wĶb»U_ìT©YÉnÛ±“§L¦Z>axÙ#ak@ÚêekU÷1 ~K±]©X©¢f(Ÿ…ÇOŸ>éSæ{ÊÔ)¤×ÀîÛM"ÁoƒîÁïšrF$à ãŒl„H€H€H€H€H€H€\J bÄ’*m*»m,šß<~Ópžƒ]¹pE½ãÇŸ›¶Ë…³Í}½¡bÿ#ÞžU¬^ÁÞa—Ë–3«Ýv¾ýö[É_8ŸÜ¼~KÇœbÆŠi·¬Ôá|0®®?ö°Û¥Ž_±ýåó—êÉË‚e+—‘С)ÉZ2 nÛ¼ºÁíŠr>$àK/ºÉˆ‘Óeõš-.óboÓ¶Ÿ´mgÜÄ—Ãt¨ú?ÿü£Êýúë¯2sæL‡ê° @Ð#€ØÚ°Þú©ä AoŽ8ŽëÜ3CØmˆU»uó¶>$Û·ì4·=Û°ßuÄWék(}ÜUïqâÅö´©‰,çt_råËéiY¿<ñìésyÿî½êâé“g2ÖBo»C¢[Ëk‚ )S¥ð¶ m܃öõãèIÀå2dH-­ZÔ“ï¾ #+Vn’wÿý1ñMG¾µøí›v|Z7 û÷é¸YH€H€H€H€H€HÀ9!áûß“ÇO=…òÂð¨Ö+¶»'x¼ø_ã±÷ÜS²å²ïM®ëňé1á)’¥ú¥=yüÌÓæŸ?{ažÓs2x±ñÏ?ÿóâ¬ó§Oþ»ï¾S O ظÁÞ6’0qe,cÈ{8ÉÁ‚÷`q9 p-Â…ó^“'”U«·ÈÜy+e×îC®í„­‘ 8M^ÖHÜi/¬ÌÉc§Ìö’¥H¦¶S¦ùêQ%j)^º˜YFo`¡âÇjxþm§Ÿö´Kë9%5Ë!ñëçÏŸåí›·æ1ËÇŸXîúzad’¥L*W/¹©P1ˆ×Žø÷¶†P2_¾¸‹ý~ùT€m¿Ü<(¸žk"F2jôo2fÜLÙ¼aŽäÌ™9DÌ9(O2B„ðòCÃêêuûö}™¿`µÌ[°JnܸãÔ´J”( ™2¦qªŽ+ >rJŽ=#Y²d‘âÅ‹»¢I¶A$@$@$@$@$@$ lÚ´IÜÜܤ`Ñ’1K†@8B÷!͘<Ë%c;|‚LŸ7Ū-ˆæÓÆýj+R¢ÚÎm„`ÑžÙs~›'MÛ4ˆÇ–6¤ïp™2fš:4gù,©ä‡ñÚ-ûÕÛëVþ!=~îêaáÀžƒræÄYU ÉF&N¨«H¢¤‰äúÕë„{±ô~GòWGÂç˜9¸Q¨XA%¸£¿µ+ÖKõ:U­j>¼ÿP g+!¯_½–ô™ÒÉÞSÞ‡ð±j€;Á‚÷`qƒÎ$Nœ<'ÏŒG®ºÝt¹àþêÕ¹ÿ‘d a×Ñ+€_øGŽœVÞã^ÕÙ½ç°äÉU xKš4¡ôï×^½öí;*sç¯r8äL½:•¤Eózþ>•ƒ'(Á½P¡B2aÂ ø(Á½JÍJÒìÇ&þÓ©zq•à¾rñj‰`$OíÒ»£¡áñ>bÀ(@ K’,±TûO N(´éÔJ&š"HúY¿Ê2lüI‘*¹JºiÝ™:vºª‡²åŒ¤žþmX,¨Vª–Lœ5^ + ÿýEvnÝ%[v1‡Ò¾ûOVå3§W‚; ´oÖQºõë"9óæ»·ï©8þ<2ëºj},_¸RÞ½}'[u•¿ÿú[*T+¯Âò;t¸#•ØŽþZ´mæªnÙN#@Á=ˆ]° >Ü&kIÔ¨‘¥`×'¸€çüŒ™KäÉÃãþx”¯ØT$ˆë­àŽy 6E¶lš(3W3äL ¼Å8(      @ ¢!´'K™LæÏ\¨^pÖÓá`0ýðáÃËè©¿Xé {uPbü‰#'e‡!dïÈPH•ûøÑ=Œ ê!ÜÌ‚Õs¬êá¸XÑ’EdÏŽ½R¯R &ŒÊ÷¿ÿ}Á^²lq©Ó°–ÕP:‹ ëWmPÇàÍŽæŽ9Iù*ådóú-Vu|»;N,5y„t2Äö÷ï?ÈOM;È7;QO ¼¶zêÈ-è]¾‡0ÖÏ„°ÉsºþO |¹b2ã×’$É×G€\5 ü¢ƒ÷ü¿ÿþëª&]ÚÎè13TôŸ~üÁÛvÛ´j ;w”™³–z[6 è3;þ\$7¯í“A:KŠIrHì›H€H€H€H€H€H€‚5ïŒØå+6/•ªµ*+oK±=MúÔ²~çj)QÆ:¬*’žnܳNº÷ï*‘£DV|,ÅöІ—ö¦½ë%Kö€ ÿÛ¶Kké5°‡ÌÿþûoÑb;Z¶k.‹ÖÍWB¼å…Í”5£Ì[9[âÆkÆœvfÉ‹$_Á<æqWnÔnPSþ<¼Y²çΦøC‡Òb{¼ñT2Õ 3ÆZy㻲¶ø |cÜSt1»:uêÈŠ+dʤAÒÖÁÓÅݺæ>~ü$+Wm6Vý>©Ð%Ù²e[·îÉq#.V¨P¡$[Ö ’}–ÄÉ (ïö3'7Y6íévŽÜ•Œ„—F"”]j¥ÔÓ‚ð„mÈ™¿°2ƒ‡L’¶mÛÊÔ©S!)‰H€H€H€H€H€HÀ´î3rÒð@R&v˜jº—œ³Š9îƒ4q3ÊË/%zŒèrõñUåù³çræäYC þGrpÌX1iJîܺ+—.\6¢DQqÓ-ãŸ;Ô€BÂÑSÇNËsá2iò$’ÁÊ+ƒ.uëú-¹yã¶$KžT°èà_çÏKç.Éóç/$¹‘¤6yªdüj,ˆ#Ÿ>ûÉÓ¿øU7~Ö.ÂÊZLbÇŽ-Ož<ñ³~ü¹áF"݈ )ãÏÔKw—.]“Vmz+a»fò-Z™ýû2«á5ú¡†Lÿ³úå«Oôé?Ú×OªÝ¢EòÊÊåÓ¤RÕæ*.¹.³pÑZ¹}c¿)z?5~4kÑC‰ûº ýìÙ3ê]óýÀÁãÒ²uo#›ó)Y¢ œ;ÅøÐ=S¿¬ð tòÔyrúÄF‰#šªƒc5k·•›ÆЇÿeŸÎœ­œÙ6²fI/‹XÇïF½n=†ËÔi ÔcJ©S'Œqàí¼9c¤XÑ|f;×?õÄ`‡¥2ݺeÄkѪ—¹êŠãã›7«‹M+C²ÑçÏ_JÇöM­Ž{µƒ…‹Þ}GÉïsVH›ÖAë1$Û3I»ÿCáÕ|yŽH€H€H€H€H€H€HÀ9Øm½Ùi1Þñ l.\8É_ø«ãÈø‚&uºÔêåHyW–Ãf.#)-, 0¤Œ%´#G&yÿæ¢ÄŒ]V­Þ¬^Æý,G­5â†Ï“Úµ*DâÚuÛYQ9°w¥||wIòåË.Œ,ЋԒ°a¿“õkgÊÃ{GdÎìÑÒ§W[SlGå8F|«×/ÎÊÛWçeÖŒ_¬Ú³Ý©T±¤<â.èïØy@jÕ,oxxïVmwïÖJy”›0Û¬:thÉ”)ä7Æ?~u<¾R ÿ×W®œ‡‚¨>yÊ<©W·²\:¿ÝH²CÝ?*ëÖÌP¢{ZmäÎûf?æyÂÆOœmðýŒ*ÔøŽ^'}ûü$%Š0ëXnLûuÚWG­r¥’ªèÌÙ;¬ŒWóÑ!gŠÍëU1ž#        `A€îÁâ2úlß~û­ÆñhÎŽm‹¬<ÎË”.l„sé­¼ÞW¬Ü¤xÝKX#Vê^¹rC…V™?w¬ò¤q£šº˜Õ;„q¼ì…‘±*hì  !màa¯êß·½2f®œ>}QÇã1 >´»ÚGH™ ®1â‡[ þfáÿ6îß$¿Œœ.‰ ¯ëߦ3ÃÇ|÷ÝwR¹R)éÚ¹… Î!îØ… n*NÚ´)tqoß3dH-±bÅË—¯«¸ôèŸF$@$@$@$@$@$@$@$@$x Pp¼×ÆßFV¼X~+±]wܽkK%¸ïÙ{ÄJp×ç§}ñ‰J|×Ç\ùïs-¶£]ÄXGŸ/^¾òU7'NžWadâÅ‹-=zyô¸óæjÿä)÷xhö:›3{”•Øn¯Œ>vçÎ%º'LOrø=A‚8röìeÁ"A¢Dñ®Ç‚$@$@$@$@$@$@$@Á‹@ö\YåÍ›·†ƒdäà51Ά‚ îÁì‚úd:ñãǶ[-A÷,ÏH¦jÏ¢‰5àéîWöƒËÖB‡¥¼½m;³D§°wïÞËÅ‹nv«"~¼éb¯@Œèî1äí³=†øò°ÿ…¼±=ïÕ~üxî‚»Ûµ[ܽÅs$@$@$@$@$@$@$Ì ,Û¸8˜ÏÓ#àA€‚{𸎾šÅé3—ìÖ?yÒÝÃ;uêdv϶ƒÚþÇf˜{cÔa]Ö¯&½xó~m‘ Ï|ØçÏî WéïÃǪx„ðá©Æ²$@$@$@$@$@$@$@$@$@@ÀïÜ“`2ìÒgλ,ã-‘¢•/^IçnCTƒU«”öYÃþ\+êT]¿~Ûªç¿þúK¶ïØ/ïßPÇ‘D5zô¨‚d¤xËÖþùçÙ²uìßÌö”öÓ¤I®ê!ɬ³vÿþcU%eÊ$ÎVey  _xõꕤH‘B4hà‹Vü¦ê»wïäøñã²aÃ9xð ñ·ìßtÄVI€üÀ¨Q£$Nœ8râÄ ï›’ ¸†=Ü]Ã1H·‚ä¡={”µë·Iþ|9äåËײnývyòä™´o×XŠͧæ÷êÕY¶|ƒ|úôY6àm¾hñ:õŽý'ÏáM–,[/{÷UÛ#†—F?ÔP‰W!ô£Ø‘£§e‘œ´hѼjÑâµòðáRfþ‚ÕÒ°A5«6Hô:hÈDiظ‹´jQOñçÿܾ_Íéu³¤b…Õ…3uò`©ß°£¤NWBªV)e¼J‹ ¯E—¿{÷S¡B¹Uß»vR±Ô¯ºÝTû+Wo–ÓgÜ“·† ZêÖ©$1cFWçl ¿8qbÉÝ»åË—/fRXÛr¶û(‹Øí‘#GRÉSmÏsŸH€HÀï<|øPnÞ¼)aì„7sE¯§N’´iÓJ„œjnøðáòË/¿ÈÛ·_Œ3gÎlü:ëe;X|>r䈑Äúï´m¥Ý»wKžýŒChúô©\½zUræÌ©‡"Þ±xˆûЯ~œ$ @  @Á=P\†€D‰âù¥]ÛF2`ÐýîÝɘ1µL×_¸TÛ©Ó¤C§Aò÷ßëCÒ¹«»<.œÛƒàîævK:v¤Dz³’±1fìLs7B„ð‚˜éÉ“'–cÇÏJ§.îmÂËüï¿¿(Ážèè‹°.݆JÙ2…%nܯñçóæÍ&Ë–L6ú¬Ê¢\¸pa¥\Ù¢Ò´I-%¶ã óŠ+º´ï8PfÍ^¦^8Ž/\%Š!ƒºHýï«à²¿LW^òzê´ùzS è)S&5ÆSÄ)Q{âĉҶ­ã¡Í¶nÝ*}ûö•B… IË–-c¼wïž‘4+Š—CÄ“[åË—— x+¸Ï˜1C†*[¶lqxØËÎy2PÈ;·zJB9ypí›7o®Ë÷uëÖIÍš5U2{=ÁÔ©SËùóç•ã‡>æÛwŸ~ÆÑo“&M ç¨R°`Aß#HÕöì™ÄŽ[Ú·o/“&M Rcç`I€H€H€HÀ–w["!t?§feÃúÙ^ÎBñçW¼,c{2kÖôòá­ýñ¶e±ÑÚ^ÈÀýüÉ){U¬ŽÕ¬Q^ðzúô¹áUÿI&Œë©`Pªd!¹t~» ŸsíÚ-‰-Š$I’P‰ôV;Û¶|ØmÏ9²ß³{k%¸oظÓaÁ}åªÍªé.‚÷—_Gø± ø7ï¾ûN¦L™â'Ý~þüYy?yòÄ©öwîÜ©ÊO˜0Á)Ï×Ñ£G ê:tÈÛþÚ´ic,t•™3gÊ?þèmyšpo㩈?ÿüSV¯^-‰'–1cÆ{Á½xñâ2þ|õ„HçÎÕ"”›››bP¯^=—]LŸ~Æ1,ŽáÒ,zôèêÉÕ˜1c†´©s¾$@!˜ÀàÞC%\øp!˜§î[Ÿ>~òm¬ïG(¸ûX6°bÇvüŸõ1¢ž†ÙütÀ æR^ü¯3|h7õè¶WâÑÿ5k·I®\Y OF÷°6^•ç9 Îb/„½hذ¡àwäÉ“'åÚµkïÔlÙ²yòòåKY»v­z* IµëÔ©c„芬<·oß®¼{³dÉ"Íš5³Z˜E‡={ö¨ðe`š={v7°=Û»w¯\¾|ÙÈ ]j×®-h&BV”+WNJ—ö:Ê¿ÿþk¯YÇ^¼x!ÏŸ?W}a!ó€H“$I<ÏóO[©˜k¾|î!âsÖRÕw6Ã#=·þ„æzƒt’ÂÅêÈÒe¤q£š^v°pÑZåy¿rÙ4/Ëñ$ „#GŽ”+V¨©þïÿ“(á\Ï¡Rà‘ ÑPÛ”‡¶öR¥J%·nÝR¢ŽŽ[²ã-Ãh ¬ÆØ±cu3R¥JA {Ö³gO9|ø°:……€… *A }Ž7N0î=z˜U!¨¡mätúôé²jÕ*ó<Ä,ˆãðÂÕ†±&J”H jCìwKƒ¸YµjUËCæ6¼y!jALuÔêÖ­+½zõRb$<ÞiÁ“r`q ÷->;¸æ¸ÿí î=R¡Œð‚PqwÓ¦MFNŸ …´àŽÏD«V­Ì„X ‚è+V,%¤"’OqB;>¯ú8÷sçι„ž(áÂýäÉ“åôéÓª_Ë«!bí7ÔaÕh»k×®1bDIŸ>½Ê]`YÛsçÎU‹uX`¢˜°Ø±ÞÖœéÃ'Ÿqô‡˜í„}mà‰?{†…xèÏž=[1¸ñ9ÇuJž<¹`ÁrÁÏÙßU¶}vèÐAfÍš¥Yp¿äʕ˶ˆÕ>D‘p÷ æ5uêT«óz× ×«{÷î2pà@Aø«þýû+‘"EòrD·Áw Š®™'ûw0†î˜óEPœ#Ç쟾‘BÅBV(:ÿ¤ëÓ¾(¸û”\¯wöìe©W¿ƒúÇöС“Æ “jFH<ºeÓ¼ >»À9ürʸ1ýdÕê-Þ î«×l•ñcûK‘"yçd8* ðG± ¸Aô8‘;¼!;¦øJ•* „1Ä7‡a"õÆUùñãÇ˶mÛdРAJÄÑð–Þ¯¨Á{ðàÁÊ3Þ·^B´@؃¸Áâ9ú…à²@p„aa"¼Îá0–I!ÆߪËСCËŽ;Œ$Ú÷eÁ‚Jœ„X1öÿöÎ<Ö®²Üë H¬(¶*"E¦Ji‘RäR•`œâð‡Sô¹ücÔ Æ 5z•„xQã^¯æÆÐÅ«Wˆæj4q ƒ€R-¥-S¡™TP@¸çùð=ùÎ:k­½öÞëìsÎÞÏ›œî½×7?ß·>ô÷½ë]ˆô¹°6£ðÔÿú¯ÇnñÀokðE|…±‚{[j‹/ß—¾ô¥Qó5¯yMzBo<Þñ,.‡õ ÍM7Ý”zDjŒƒ%Ö8/ñÍ=¹yº!Ïô+VÛ·oO[”áþصkWò\æŠ5×ñ~Ç'ExÂ{ÞóžäÏÁÕù矟î3Þ/¶{÷ît Æï/|á ÉSû‚ûÁ=9j”ãxˆÐyiƒÃîÿÜúmc{œö–/_ž¼Ûaúõ¯}Æ!`ÞŸø¾qãÆ‚ùƒ}ý€”ÓN;-À!z¯Zµ*éw¯ŠvøD4gÿÅ®»îºj‹=¨ÉÈχ±}öÙé  \†43O|àHë=þòd¢š$ q$°áä)øÓ$ ñ% à>¾sÛ82¼ØøãÖéÇ}ÉÌ C÷ß¿Ær&Gà¬3ÿµØøÎÓzVrñ·ÿÛÿ“Ñ“’$ I" /<¼è¢‹¦‡Ž`ýâ¿8…¦xÛÛÞ–BPD"ÿ]‹ ?ýéO ^:ú¾0’gˆÝÓ§¾ì³Ï>é'/’ìeâØ‡?üáâu¯{]úŽ I8B! P…7(‡ü6ÏsÄGzÙI'=ö²í+®¸"eÅ›0½Œ§÷7rÙ+¾©,a)8Ì@0CH…£6^˜W[î§Xï®lÚ´)]GìÎ 1œµž ñ£x†®ˆÐN¹áÍþîw¿;^qEH§0¨Â“9®ñ÷aDx¡pÜóx<ó÷ÜÎ9çœt/á!Ï!QúˆöÜ7b•ýOò·¼å-)iåÊ•ÅË_þò4îóÎ;oÆËSûmcÐ{œŽ0~þbÏ*÷;~³0w¯zÕ«f½g‚±#ØóÂUæ§‚o›½*ÊðÉ6$ÄÓ ˆä½ ±ô¡w›PL¹ñ!—ðP祦<ò‰O|"•#|H•ï%òq?åak¸†P‹([g¾e#,/¥®*OíòËSi£Üf׿éÆËE댴mÛ¶¥°ZK—.‘­Ì·Í^E}ý„—âé ÖL9d׌Žüó7¼—õÆSÆpЯI@€$ ÅJ ÷³Ò‹udö[€$ ±#°yóæÊ1¯+‡¶¨Ì<ÏÃ{7Aâ\t)bÇÇ‹cûi#úáFú)kÞ…MàHa™ù‚rþÇý…艗{näY³fMz'O=ðäïA@¼F0Å3¹jñ²^ÂÐàå~×]w%ïv^ŽÚÅ!b;!ÿ$¥*çIDAT›ªÚÍûž' ![x)k>n¾ó¾<ÿù6H”Ë{<¶lÙÝœñ¡¤ÍSÛgdìãO ê¥Îk¾ª*<ñ)S~2¡œÁŸP>„õÁë/¸à‚ʧ&Êeý- H@€*÷…:3öK€$ Yˆ©Œp—"ÚO~ò“ê`Ù²eyÒ‚üÎ äð¶-±Œãð œÖïoÂ?`wÞyg¿ES( ñâKm¼ðbTðXæ€*ÿ#4 á_ˆ»NÜö0¼ÔyWb}ìÄ,'V:|žy8ôyï{ß[ äú…ØíÜÃ]ýä¨xØ— ˜ñ½éMošNЏõ„89æ˜cfŒxûö&?lè·hl.ïq¼Æy¹,"u•˜ÍSÜó¯}ík£;C}"àÒç­o}kšëœO]Å_üâSø!ʰ¦âiˆºü^—€$  H@ãFÀ2ã6£ŽG€$0Æìx©'bÒòåËSüib52‘' ÐðVåE‰ñÒE^¤ˆ—¿’rxd月v;wîL/lŒz‰¿nݺôoØljoý†7¼¡8à€’×-/rÄ.¾øâ›8¼R¹†¨÷ŠW¼¢øÞ÷¾Wð¢×½èE©oôùÊ+¯L/Y¸Úä¿ð SøŒ¸öÙÏ~vÚsáíŒ3ΨŒ‘ÌáÊ+Š[o½5 ¢mBGÐâ)^ÌÔ]ž‡<Úâ$ðÝï~7ÅUç~ùá˜bªÇ» ðçE•„á;á`³?üðô›ðóý#ùHº°y1u"º×­•7¼(•õ‹Ç{Uˆ#„c^ö‰qæ!mbýÓÓ§žzjò‚>ûì³Ó=ôÉO~2½˜û5Ï=Ih Î?ÿüTçm·Ý–®ñ^Æ‹5{÷+FÂäJ‰<^ÆÌ‹ûi#UôÏú¹Ç9ü`ŒñT ñÒ1ÞIñóŸÿ<}GÀ?í´ÓÒÜÀ8øp`>>øÁ&Q›÷Wp˜ò•¯|%=pî¹ç¦²ü3è^EY˜ñv饗¦ëñ»ê“'ÂØvíÚUàq¯I@€$ I!°dê¼>: ƒ}ãßX|ãß(þó3-Þñö·L£&žÀ¿}ì?Šû™âïxGñ¹Ï}nây@‹™Ó;ßùÎSüË_þrÚˆQŒøtòÉ'ÿøÇ“¸c|ÙË^VüèG?ŠŸ3>Ü #„A$¾ùÍo.¾úÕ¯ÆÏÊOÂNÐŒ˜Ã!ˆzqý%/yIñÑ~´@ì«Ov<| ¹‘ÞÀ´‡è!1Ž:ê¨$ÚŸyæ™é0üˆß‡rHqûí·çŧ¿ãA¼uëÖÚP™±þøÇ?NÂþtÁ†/pƒ‚ä·¾õ­†œ&-6¼rÇŽ©ÛˆÂ¬WÖ Æý„Ç:¡b0B{p_±~9ÔáÞàE§<…ÿ÷:XcŸÿüç“X VüƒxÍýÁVUìq„{êyä‘GRiúÅ:¼ÿþû‹C=tÚ“›C2„’0îÂÕ è‡`Íuö„÷¿ÿýÅ+_ùJ~&í¸×x+kû¥/}iJ'†8qíÛ09Å„×iÛFª(û§í=λ8°x衇²Ò3¿"¸aˆÚìqÈuö!öIE(6Ì^EÌ?û./6åP†³6—jožƒ84 H@£ ºÏ¿æ¼âŒ·Ÿ>Š&mC‚ÀuÛ®/^pì))DáÝwß=DM ªèƒSÿy_÷5'vFè’€‚{—4­KóK w^ŠŠ=.† Î˜ð&DG׆7*"*á2>õ©Oµªa’°z”Õ$@èþËÁñXFœ^¹reÏu‹w4![Þõ®wx£Ï…ñä !šî»ï¾$J—Ÿ^é¢ÍAÛ˜ë{œùàIžZ`>8(™ ムYÚ‡2¬™x—D›2æ‘€$0,÷a Z^£%0΂»!eF»–lM€$  L Ì !;æÊžÿüç'/fBVóÝ&ÃÃøÛßþvq '(¶7š°4„Vþ0B ñÕ¶†:O•¼ï}ïk[¤ï|ˆÌˆúsiƒ¶1×÷8ó±~ýú¹zª»_áa¾ß2s>€$  H@#" à>"Ð6# H@À`™±Õ‰kŒ÷êÞ{ï]¼þõ¯O!«urJ↸Մ‚à%†MFügâió^“À ãÈ;DziÓ¦$ó~‚W¿úÕƒVi9 H@€$  , î‹fªì¨$  H`2 ðRÀx é9眓 àmÊKøðàÖš lذ!Ÿþæ7¿ÙSp'®õ§?ý黹VS%PM€—•·;ⱓ‹3^jJx^v¬I@€$  H`œ (¸óì:6 H@ÀøÎw¾“âFçCÙk¯½Š¥K—æ—üÞ@ଳÎ*6nÜØã±$^lÉ‹^5 J€'Pˆ÷]~ (O¥ô i4h›–“€$  H@ÀB" à¾fþH@€$0‹±€—-[6ëºú#ÐFHo“§¿VÍ=‰8ãO“€$  H@À$˜›×ØO"IÇ, H@€$  H@€$  H@M@Á}¢§ßÁK@€$  H@€$  H@@WÜ»"i=€$  H@€$  H@€$0ÑÜ'zú¼$  H@€$  H@€$  tE@Á½+’Ö# H@€$  H@€$  H@M@Á}¢§ßÁK@€$  H@€$  H@@WÜ»"i=€$  H@€$  H@€$0ÑÜ'zú¼$  H@€$  H@€$  tE@Á½+’Ö# H@€$  H@€$  H@M@Á}¢§ßÁK@€$  H@€$  H@@WÜ»"i=€$  H@€$  H@€$0ÑÜ'zú¼$  H@€$  H@€$  tE@Á½+’Ö# H@€$  H@€$  H@M@Á}¢§ßÁK@€$  H@€$  H@@Wö誢ÅRÏÕ›¯-¾qÑÿ-–îÚO H`Û¶Ý8Di‹J@€$  H@Àb#°eó5ÅÅ}w±uÛþJ`âÜ~Ûc;æ‰Ü÷Øã±¡^°é þ4 H`rì¹çž“3XG* H@€$  H` „îsá¦ÿ)øÓ$ ÅA îÝÅÑÛv½œÁ}ãÆÅ=÷ÜS<üðÃíȘK ûí·_qÆgŒÅX„$  H@€$  TP÷©æâU ,dK–,)N=õÔ…ÜÅú¶äÑ)¨¤…$  H@€$  H@€$  H@€ÀƒS‡ûúÒTƒ$  H@€$  H@€$  H  î@´ H@€$  H@€$  H@€‚»k@€$  H@€$  H@€$Ð÷ Z…$  H@€$  H@€$  H@ÁÝ5  H@€$  H@€$  H@耀‚{­B€$  H@€$  H@€$ àî€$  H@€$  H@€$  t@@Á½ˆV! H@€$  H@€$  H@Ppw H@€$  H@€$  H@€:  àÞD«€$  H@€$  H@€$  (¸»$  H@€$  H@€$  H@Ppï¢UH@€$  H@€$  H@€Ü]€$  H@€$  H@€$ (¸wÑ*$  H@€$  H@€$  H@ î® H@€$  H@€$  H@@Ü;€h€$  H@€$  H@€$ w×€$  H@€$  H@€$  H  î@´ H@€$  H@€$  H@€‚»k@€$  H@€$  H@€$Ð÷ Z…$  H@€$  H@‹À£>ZÜ{ï½ÅŸþô§ÊÎ÷J¯,äʼn àÚèfš|ðÁtþýïï¦Âjan5 C`a [V€$  H@€$ ÅAàw¿û]qóÍ7Ë–-+Ž:ê¨ÅÑé9î%L¶mÛVvØaÅ~ûí7«µ^é³ Ô\¸ÿþû‹;v{ï½w±víÚš\3/z¾8x¸á†Š}÷Ý·X½zõÌÎøk|müãÿè{~gU8äÖ1G¬å§<å)CÖ6ºâ[·n-þú׿'tRÁwø#<²xÒ“ž4ºNLµ„àÏþx÷Ýw<òH±téÒâÄOircüã ö1ÖØãÿøbŸ}ö)–/_^<á Ohì6e(ûç?ÿ¹X²dIÚ_8à€Êý¶®"öNAžóœçÔe™¾N{øÃЇzhºŸý¶7]Ù_܇€gQ H@€$  H@Àb!ð·¿ý-‰Yˆ¾ýBÔ¯ýëTä¸ãŽ+÷¸™Ë÷Jï§­Qç½ýöÛÓxžö´§U6Ý+½²PÅE!$–­‰Ý óUn£íïè#bšÖ›@¾6!«æ·w-ÝåøË_þ’úÀ<.Cˆ…Û3ŸùÌt>üðÃó6†›nº©`N1DäØçšîÑÅÂy˜~²]wÝuLeã€âYÏzV±råÊrRÁ!xwÜqǬ4„÷ƒ>¸xö³Ÿ=+­|á(î¼óΞÂ>íqèôûßÿ¾\EúŸþô§GqDú>+Ã\PpŸ¨V) H@€$  H@x"z`U¡z¥/Tx@"ø=õ©O-öÜsÏYÝì•>«@Ã…=öØ£ÀË²ì ºXÙ5 u"’Êk£n~G ƒ'4‰÷Úk¯Q6;T[!Æ"†Î·…P‹wý3žñŒéîLú=zÍ5×÷Ýw_Ú#9äô„O$0wˆð·ÜrKZs93àqïyÏK^²¬jXK±nðºÅHƒWˆ£¬Ç+V‡~ø ¯Û”ùŸÿT­˜Ï˜ßÆÞ1ªKýï©‹þ# H@€$  H@Àb$€øð›ßü&‰Cx-òUD9D„9D‹x™âEˆ<Œ5¾çŸñ½*kԋ膀‡¸I~Ä(D#BÄC„{Ä¿h—rØÖ­[Ó¡@4ˆ%å”C°CH}îsŸ;C°¤-þªâ²?Vk1[¸Êû’<\ˆ6åtúŽ ÎX0„Ò®Ñ7øÑ>Ÿa”+÷‰kü…Å÷øŒëýÌe¢ý\´Šºâ3æ$ïc¤Ñb.B¼…ãe–Ìì9L¨òx:ÊŸˆ½Äb†s‰ÈˆøÅº£Ö#‡¹ðFeý(ƒ`ˆ¸Çz!2!Öçcã BVÐb&sCYÖ ã¢^æ‘’12\ƒAÝaEÕÚ`ÎÊóKŸ¸ÎÁ}Á8´`íÓ6b("5l¤C4¤/ŒÔGaU6UÒ™›cÜùœÂ!žñÐvÔE½üÁ—^… ÒgÊÆºã3BDåOÖãG4fnˆÛï^ãïgŸa¬üåFÿƒi9=òÆ'å˜Êph³yóæô;¯ï4!œcìKqÇ:fØçØÇâ:ù¨sÇŽÓ{ k–ùc²~Ø«9”(ïþ¬qÆÀÚfÞaM^ÖÚªU«Ò¡mô2Ö"Âx¬›r~î)¸cù~^ä!À—Ëñ›4æ9òFÚË­i|´Íâ<÷gÞ†<\ÃØªÖ[Jìø÷ŽZ$  H@€$  H`!@äAd'&o<ˆ;;wîL ÂB&^/xÁ ’ rÉ%—¤!tÒIIÀŒñõJ||"z Ü Ö"…!ñ‡G,m†§'Âø$Ç|h¢ ‚+Âyr¡á‘†1VÂbeÞÈÛ”Ž×(‚Â/"iîù‰@ˆ¸ Ë^Ö†mÔÑv¾–»0D+ÄC<¥ó®#Þ’†X÷ô@¹ðàa’¹]½zõôââ2‚4uâ Û(ƒ°ˆ‡øpˆ!v"¤Gè Ò«X£ô£Jìä:F¨&CÐGlÇka= †ˆâÜë¬é\p¤ÏQ/Ÿ¬Wþè7üBã0!ò…š¯¯Hã³ß½bв~°‡Áãg?ûYª†'ò}A¹nÿËÛEè†5@¬Ç›ÔáÌýÉzË×*ùñ°'âsìÇÔKöÊ1w¹g:|9cÿc-ÇA%kùƒ7÷L¾v`ŠðÏZ#=o+GþCÐ&£÷f>ÇñXÎ1]Èþ !=òfI}…Œžp¦nöe~Â'WFe³‘FÕ²íH@€$  H@€$0/©‘ .‚|pòŠDŠ]v!o̲ƒ@ÂBj‚$F¹¯"ß„®@0+÷!˜p"k×®ì3>žØ¢Í‰S?êÒ`C`‚_.¶S¢& çÂF=_ùœÄxNyñ+k%:"­éÑá !™Ã–ü`Ñ !š9fÝá…‹Qq›tÊ„ØNž»ŒðÆêÂM Pæb;yY3Ὄwu.bF:ãc}pÀP¶ºµQÎÇoƃ˜!\Ò^n¬]Xúƒ€":yblùµ(Ë}Áša>®W}Æ=Tö¦MØÐ¾ÓWl˜>GûxÕs˜Å}_Ö m„ÑB2(íWY¿{E^Ç0eózúýβ¿­œÚÏàM?0®c0ÉÅv®q/Äa/a0Š—Ž"—×)õ³–1„zŒ2&Áñ9ÛIg¿cbm#Êk¬M¨°ü£NöJŒ{©Î"-òÖåk{ñÆS)ܧfq¨„ØgöÐ|i[ï ùêG>h–“€$  H@€$  H`Á@t/ò¼“\Gúò:ÌëGðÉÅÖ<-„jÄ„V,Ä)ú‚çx¹Oˆ˜ÄHîå…™·Ãw¼@+Àª¬.1l rú¼Žr(ƒ0¡(éÑ^•X·6²f§¿"´²fYóuk”û-×3°¾(‡°Î=‘[pryZù{ÜCÿ< ’ãäþá/îËaúu3ǰUsM<އëÖyúÝ+(6LÙ¨cOæ,?ÄŒ:èñÝyb¤lˆß!Èó=,æ9ŠÃ—H‹Ožàž!D Æ!ë5UÕòsÖs0ˆÑOF´ùÎÞ]îc>–AÚ¤ ‡8†G%yðÈܼyóôKô¸†ðƒ¸‰€õs=¬W:ùh'^xà 7$±+Ê#¬ÝxãIŒÎyDzÝgž·Šm]¹¦ë°`ŒpŽƒŒÈÇ {çåC…-Á'wx_G]UÜI#|Þ´×_}dOŸxaß|óÍéoå˜sâ0¢&¼ 1á(Ìz‰±Ã.­ÍÚ¨jŸõñ¤c™'Ê×^{m;‚a™'u™ƒÖnÌ[ˆðUmæ×h¡Æ”-{ÙÃᣠ¬‹>§Š*þa¾XUã,gïw¯ÈËS6¯'¾sR¦X¾Qo¬ý¸ž,q/Ä“8Ü3åý{‰}‹õ€Øëmûöí³ÊJ'ÖP›9 î8Ôb_¦þ¶Æák—}‘þGʳ–Ù?1Ö}ˆåé€ÿÄÁk¬|°œ¹™¶c°+ÓÅÚÓš.â H@€$  H@€&…"BÉ/ùË$„7^äØ+=8áɈâå—_žDY7”å~aî’Y ùÌiÜÛ”!?ð§ÊpïQw/£o±7ó”QÝ“FQð±wÐÿ#k=’1aqGùa>aøyÒ!ŸñÆCý„õaü£0÷QP¶ H@€$  H@À"&€ ˆw$B'‚bLn½ÒÉ‹8а‡ð„(†HCZxÇuÙÕ«W'/KCÊäåN(“‹…Q6ÿ¤¯ˆx]"n–­Wzž!‰´"è h#b!”!†!ú ¸_uÕU©Hx¶æå«¾·aWU®é¢‚!!˜/¼aZoiÏê:#cD ‡[„<¡>ÄL„´*Žuõq1 F þÅÓ ¤!Ä! “žm­_¿>yȆàL:ëñl®Åö~ÖFÞ÷øëA•ƒ>Ig}ÐâzüÎXßÌb%ß©³­±æ×¬Y“î!îÜËž6™8ækµ‹>—ûÇý‹åâp9Oþ»ß½¢«²y=ù÷aîÑx)÷ 똿XÃìoìc\cm0?´…1'Ç|º¹ŽùØ;öš<¬×™»µkצƒñã`’ûþx«·YCQŽzÙCzë37ÖÞºuëÒýK]6`Œ{ž}$ú¼Ü ß©zùï qX@}ˆï< CŸFeK¦ÔþGGÕ˜íH@€$  H@€$0YðŠG˜Fø ï\ÄÄL¼Ðs±¯Ž Ò^¡ü!á5wyC,BìAØB¨-[¯ôrþü7ýBhBÌÂøMÜl<—O>ùäVÂV^_×ßé¬Càë·~ÆÈøªØõ[ùaD‚[°Kþá°'<²Û† UµNfmT5éCÙs¼)9±u‚fƒô©KoØò8ë~·]uåË×ç‚g¹ºßˆ®m·Êå»è3O jýîy;ÔÍëéâ;›Ö}SísHÒö1úËÕõýu÷ó9ì>ÔO[äeÜ]Ćï·Ý<¿‚{NÃï€$  H@€$  H ‚B1¡ÿ€%7¼¶#Îq9ÔCžÏï€$0ÞÜÇ{~$  H@€$  H@ Þ5/¡$†;/&$Î8ž«„ƒ F6a#ð¬l«º£nY$  ,  î h2ìŠ$  H@€$  H`Üðx?¡,Bˆ•aÙòR¾ãŽ;.½„’Š"¼‡W™âÝŽ7¼& ŒŽÀ8í3££fKsEÀ—¦ÎYë•€$  H@€$  H`l ð"@^BÉç(^8¶ ˜$ ñ!^šªà>>êH$  H@€$  H@€$  H`~$ÁýqóÓ¶­J@€$  H@€$  H@€Æ‹€‚ûxͧ£‘€$  H@€$  H@€$ y" à>OàmV€$  H@€$  H@€Æ‹€‚ûxͧ£‘€$  H@€$  H@€$ y" à>OàmV€$  H@€$  H@€Æ‹€‚ûxͧ£‘€$  H@€$  H@€$ y" à>OàmV€$  H@€$  H@€Æ‹€‚ûxͧ£‘€$  H@€$  H@€$ y"°ÇT»ÎSÛ6+ H@€$  H@€$  H@êìã0‹ŽA€$  H@€$  H@€ÿß-×*03Ù®IEND®B`‚bpftrace-0.14.0/images/bpftrace_probes_2018.png000066400000000000000000007522231413460502400211610ustar00rootroot00000000000000‰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.14.0/man/000077500000000000000000000000001413460502400141345ustar00rootroot00000000000000bpftrace-0.14.0/man/CMakeLists.txt000066400000000000000000000001431413460502400166720ustar00rootroot00000000000000add_custom_target(man ALL DEPENDS adoc_man man_man) add_subdirectory(adoc) add_subdirectory(man8) bpftrace-0.14.0/man/adoc/000077500000000000000000000000001413460502400150425ustar00rootroot00000000000000bpftrace-0.14.0/man/adoc/CMakeLists.txt000066400000000000000000000013321413460502400176010ustar00rootroot00000000000000find_program(GZIP gzip REQUIRED) find_program(ASCIIDOCTOR asciidoctor REQUIRED) file(GLOB FILES *.adoc) set(GZFILES "") foreach(FIL ${FILES}) get_filename_component(NAME ${FIL} NAME_WE) set(MANPAGE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.8) set(GZ_MANPAGE_FILE "${MANPAGE_FILE}.gz") add_custom_command(OUTPUT ${MANPAGE_FILE} COMMAND ${ASCIIDOCTOR} ${FIL} -b manpage -o - > ${MANPAGE_FILE} DEPENDS ${FIL}) add_custom_command(OUTPUT ${GZ_MANPAGE_FILE} COMMAND ${GZIP} -c ${MANPAGE_FILE} > ${GZ_MANPAGE_FILE} DEPENDS ${MANPAGE_FILE}) list(APPEND GZFILES ${GZ_MANPAGE_FILE}) endforeach() add_custom_target(adoc_man DEPENDS ${GZFILES}) install(FILES ${GZFILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man8) bpftrace-0.14.0/man/adoc/bpftrace.adoc000066400000000000000000001556011413460502400174700ustar00rootroot00000000000000= bpftrace(8) :doctype: manpage :toc: true //// Style guide: - one sentence per line //// == Name bpftrace - a high-level tracing language == Synopsis *bpftrace* [_OPTIONS_] _FILENAME_ + *bpftrace* [_OPTIONS_] -e 'program code' == Description bpftrace is a high-level tracing language and runtime for Linux based on BPF. It supports static and dynamic tracing for both the kernel and user-space. When _FILENAME_ is "_-_", read from stdin. == Examples List all probes with "sleep" in their name:: ---- # bpftrace -l '*sleep*' ---- 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' ---- == Supported architectures x86_64, arm64 and s390x == Options //// Custom prefix to easily link section //// :idprefix: flags_ === Output format *-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. *-f* _FORMAT_:: Set the output format. Valid values are:: *json* + *text* *-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. *--no-warnings*:: Suppress all warning messages created by bpftrace. === Tracing *-e* _PROGRAM_:: Execute _PROGRAM_ instead of reading the program from a file *-I* _DIR_:: Add the directory _DIR_ to the search path for C headers. This option can be used multiple times. *--include* _FILENAME_:: Add _FILENAME_ as an include for the pre-processor. This is equal to adding '#include _FILENAME_' to the start bpftrace program. This option can be used multiple times. *-l* [_SEARCH_]:: 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. For more details see the <

> section. *--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. *-k*:: Errors from bpf-helpers(7) are silently ignored by default which can lead to strange results. This flag enables the detection of errors (except for errors from 'probe_read_*'). When errors occurs 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));} ~~~~~~~~~ ---- *-kk*:: Same as '-k' but also includes the errors from 'probe_read_*' helpers. === Process management *-p* _PID_:: Attach to the process with _PID_. If the process terminates, bpftrace will also terminate. When using USDT probes they will be attached to only this process. *-c* _COMMAND_:: Run _COMMAND_ as a child process. When the child terminates bpftrace stops as well, as if 'exit()' has been called. If bpftrace terminates before the child process does the child process will be terminated with a SIGTERM. If used, 'USDT' probes these 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 PID is available to programs as the 'cpid' builtin. + The child process runs with the same privileges as bpftrace itself (usually root). *--usdt-file-activation*:: activate usdt semaphores based on file path === Miscellaneous *--info*:: Print detailed information about features supported by the kernel and the bpftrace build. *-h, --help*:: Print the help summary *-V, --version*:: Print bpftrace version information *-v*:: verbose messages *-d*:: debug mode *-dd*:: verbose debug mode //// ! ! ! This prefix reset must be at the end of the section, else cross references break ! ! //// == Environment Variables Some behavior can only be controlled through environment variables. This section lists all those variables. //// Custom prefix to easily link section //// :idprefix: envvar_ === BPFTRACE_STRLEN Default: 64 Number of bytes allocated on the BPF stack for the string returned by `str()`. Make this larger if you wish to read bigger strings with str(). Beware that the BPF stack is small (512 bytes). Support for even larger strings is [being discussed](https://github.com/iovisor/bpftrace/issues/305). === BPFTRACE_NO_CPP_DEMANGLE Default: 0 C++ symbol demangling in user space stack traces is enabled by default. This feature can be turned off by setting the value of this environment variable to `1`. === BPFTRACE_MAP_KEYS_MAX 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. === BPFTRACE_MAX_PROBES Default: 512 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 or crash the system. === BPFTRACE_CACHE_USER_SYMBOLS Default: 0 if ASLR is enabled on system and `-c` option is not given; otherwise 1 By default, bpftrace caches the results of symbols resolutions only when ASLR (Address Space Layout Randomization) is disabled. This is because the symbol addresses change with each execution with ASLR. However, disabling caching may incur some performance penalty. Set this env variable to 1 to force bpftrace to cache. === 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_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_PERF_RB_PAGES Default: 64 Number of pages to allocate per CPU for 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. //// ! ! ! This prefix reset must be at the end of the section, else cross references break ! ! //// :idprefix: _ //// Custom prefix to easily link to a section //// :idprefix: language_ == bpftrace Language === Overview The `bpftrace` (`bt`) language is inspired by the D language used by `dtrace` and uses the same program structure. Each script consists of an preamble and one or more action blocks. ---- preamble actionblock1 actionblock2 ---- Preprocessor and type definitions take place in the preamble: ---- #include #define RED "\033[31m" struct S { int x; } ---- Each action block consists of three parts: ---- probe[,probe] /predicate/ { action } ---- Probes:: A probe specifies the event and event type to attach too. Predicate:: The predicate is 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)); } ---- This script has two action blocks and a total of 3 probes. The first action block uses the special `BEGIN` probe, which fires once during `bpftrace` startup. This probe is used to print a header, indicating that the tracing has started. The second action block uses two probes, one for `open` and one for `openat`, and defines an action that prints the file being `open` ed as well as the `pid` and `comm` of the process that execute the syscall. See the <> section for details on the available probe types. === Identifiers Identifiers must match the following regular expression: `[_a-zA-Z][_a-zA-Z0-9]*` === Comments Both single line and multi line comments are supported. ---- // A single line comment i:s:1 { // can also be used to comment inline /* a multi line comment */ print(/* inline comment block */ 1); } ---- === Data Types The following fundamental integer types are provided by the language. [cols="~,~"] |=== |*Type* |*Description* |uint8 |Unsigned 8 bit integer |int8 |Signed 8 bit integer |uint16 |Unsigned 16 bit integer |int16 |Signed 16 bit integer |uint32 |Unsigned 32 bit integer |int32 |Signed 32 bit integer |uint64 |Unsigned 64 bit integer |int64 |Signed 64 bit integer |=== ==== Floating-point Floating-point numbers are not supported by BPF and therefore not by bpftrace. === Constants Integers constants can be defined in the following formats: - decimal (base 10) - octal (base 8) - hexadecimal (base 16) - scientific (base 10) Octal constants have to be prefixed with a `0`, e.g. `0123`. Hexadecimal constants start with either `0x` or `0X`, e.g. `0x10`. Scientific 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, `1e-3` is not valid. To improve the readability of big literals a underscore `_` can be used as field separator, e.g. 1_000_123_000. Integer suffixes as found in the C language are parsed by bpftrace to ensure compatibility with C headers/definitions but they're not used as size specifiers. `123UL`, `123U` and `123LL` all result in the same integer type with a value of `123`. Character constants can be defined by enclosing the character in single quotes, e.g. `$c = 'c';`. String constants can be defined by enclosing the character string in double quotes, e.g. `$str = "Hello world";`. Characters and strings support the following escape sequences: [cols="~,~"] |=== | \n |Newline | \t |Tab | \0nn |Octal value nn | \xnn |Hexadecimal value nn |=== === 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. === Operators and Expressions ==== Arithmetic Operators The following operators are available for integer arithmetic: [cols="~,~"] |=== | + |integer addition | - |integer subtraction | * |integer multiplication | / |integer division | % |integer modulo |=== // TODO: Words about integer conversion when types mismatch ==== Logical Operators [cols="~,~"] |=== | && | Logical AND | \|\| | Logical OR | ! | Logical NOT |=== ==== Bitwise Operators [cols="~,~"] |=== | & | AND | \| | OR | ^ | XOR | << | Left shift the left-hand operand by the number of bits specified by the right-hand expression value | >> | Right shift the left-hand operand by the number of bits specified by the right-hand expression value |=== ==== Relational Operators The following relational operators are defined for integers and pointers. [cols="~,~"] |=== | < | left-hand expression is less than right-hand | \<= | left-hand expression is less than or equal to right-hand | > | left-hand expression is bigger than right-hand | >= | left-hand expression is bigger or equal to than right-hand | == | left-hand expression equal to right-hand | != | left-hand expression not equal to right-hand |=== The following relation operators are available for comparing strings. [cols="~,~"] |=== | == | left-hand string equal to right-hand | != | left-hand string not equal to right-hand |=== ==== Assignment Operators The following assignment operators can be used on both `map` and `scratch` variables: [cols="~,~"] |=== | = | Assignment, assign the right-hand expression to the left-hand variable | <\<= | Update the variable with its value left shifted by the number of bits specified by the right-hand expression value | >>= | Update the variable with its value right shifted by the number of bits specified by the right-hand expression value | += | Increment the variable by the right-hand expression value | -= | Decrement the variable by the right-hand expression value | *= | Multiple the variable by the right-hand expression value | /= | Divide the variable by the right-hand expression value | %= | Modulo the variable by the right-hand expression value | &= | Bitwise AND the variable by the right-hand expression value | \|= | Bitwise OR the variable by the right-hand expression value | ^= | Bitwise XOR the variable by the right-hand expression value |=== All these operators are syntactic sugar for combining assignment with the specified operator. `@ -= 5` is equal to `@ = @ - 5`. ==== Increment and Decrement Operators The increment (`\++`) 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. E.g. ---- $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. === Variables and Maps bpftrace knows two types of variables, `scratch` and `map`. 'scratch' variables are kept on the BPF stack and only exists during the execution of the action block and cannot be accessed outside of the program. Scratch variable names always start with a `$`, e.g. `$myvar`. '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. ==== Associative Arrays Associative arrays are a collection of elements indexed by a key, similar to the hash tables found in languages like C++ (`std::map`) and Python (`dict`). They're a variant of 'map' variables. ---- @name[key] = expression @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 snippet creates a map with key signature `[int64, string[16]]` and a value type of `int64`: ---- @[pid, comm]++ ---- === Variable scoping // TODO === Pointers Pointers in bpftrace are similar to those found in `C`. // TODO, not true yet === 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. ---- i:s:1 { $a = (1,2); $b = (3,4, $a); print($a); print($b); print($b.0); } ---- Prints: ---- (1, 2) (3, 4, (1, 2)) 3 ---- ==== 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]); } ---- === 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 struct 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); } ---- === Conditionals Conditional expressions are supported in the form of if/else statements and the ternary operator. The ternary operator consists of three operands: a condition followed by a `?`, the expression to execute when the condition is true followed by a `:` and the expression to execute if the condition is false. ---- condition ? ifTrue : ifFalse ---- Both the `ifTrue` and `ifFalse` expressions must be of the same type, mixing types is not allowed. The ternary operator can be used as part of an assignment. ---- $a == 1 ? print("true") : print("false"); $b = $a > 0 ? $a : -1; ---- If/else statements, like the one in `C`, are supported. ---- if (condition) { ifblock } else if (condition) { if2block } else { elseblock } ---- === Loops Since kernel 5.3 BPF supports loops as long as the verifier can prove they're bounded and fit within the instruction limit. In bpftrace loops are available through the `while` statement. ---- while (condition) { block; } ---- Within a while-loop the following control flow statements can be used: [cols="~,~"] |=== | continue | skip processing of the rest of the block and jump back to the evaluation of the conditional | break | Terminate the loop |=== ---- i:s:1 { $i = 0; while ($i <= 100) { printf("%d ", $i); if ($i > 5) { break; } $i++ } printf("\n"); } ---- 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: ---- i:s:1 { unroll(3) { print("Unrolled") } } i:s:1 { print("Unrolled") print("Unrolled") print("Unrolled") } ---- //// ! ! ! This prefix reset must be at the end of the section, else cross references break ! ! //// :idprefix: _ == Sync and Async 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 async behaviour can lead to some unexpected behavior as updates can happen before user space had time to process the event. 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 ---- == 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 `-kk` 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 `kptr()` and `uptr()` functions. == Builtins Builtins are special variables built into the language. Unlike the scratch and map variable they don't need a `$` or `@` as prefix (except for the positional parameters). [%header,cols="~,~,~,~,~"] |=== | Variable | Type | Kernel | BPF Helper | Description | `$1`, `$2`, `...$n` | int64 | n/a | n/a | The nth positional parameter passed to the bpftrace program. If less than n parameters are passed this evaluates to `0`. For string arguments use the `str()` call to retrieve the value. | `$#` | int64 | n/a | n/a | Total amount of positional parameters passed. | `arg0`, `arg1`, `...argn` | int64 | n/a | n/a | nth argument passed to the function being traced. These are extracted from the CPU registers. The amount of args passed in registers depends on the CPU architecture. (kprobes, uprobes, usdt). | cgroup | uint64 | 4.18 | get_current_cgroup_id | ID of the cgroup the current task is in. Only works with cgroupv2. | comm | string[16] | 4.2 | get_current_com | `comm` of the current task. Equal to the value in `/proc//comm` | cpid | uint32 | n/a | n/a | PID of the child process | cpu | uint32 | 4.1 | raw_smp_processor_id | ID of the processor executing the BPF program | curtask | uint64 | 4.8 | get_current_task | Pointer to `struct task_struct` of the current task | elapsed | uint64 | (see nsec) | ktime_get_ns / ktime_get_boot_ns | Nanoseconds elapsed since bpftrace initialization, based on `nsecs` | func | string | n/a | n/a | Name of the current function being traced (kprobes,uprobes) | gid | uint64 | 4.2 | get_current_uid_gid | GID of current task | kstack | kstack | | get_stackid | Kernel stack trace | nsecs | uint64 | 4.1 / 5.7 | ktime_get_ns / ktime_get_boot_ns | nanoseconds since kernel boot. On kernels that support `ktime_get_boot_ns` this includes the time spent suspended, on older kernels it does not. | pid | uint64 | 4.2 | get_current_pid_tgid | Process ID (or thread group ID) of the current task. | probe | string | n/na | n/a | Name of the current probe | rand | uint32 | 4.1 | get_prandom_u32 | Random number | retval | int64 | n/a | n/a | Value returned by the function being traced (kretprobe, uretprobe, kretfunc) | `sarg0`, `sarg1`, `...sargn` | int64 | n/a | n/a | nth stack value of the function being traced. (kprobes, uprobes). | tid | uint64 | 4.2 | get_current_pid_tgid | Thread ID of the current task. | uid | uint64 | 4.2 | get_current_uid_gid | UID of current task | ustack | ustack | 4.6 | get_stackid | Userspace stack trace |=== //// Custom prefix to easily link to a function //// :idprefix: functions_ == Map Functions Map functions are built-in functions who's return value can only be assigned to maps. The data type associated with these functions are only for internal use and are not compatible with the (integer) operators. Functions that are marked *async* are asynchronous which can lead to unexpected behavior, see the <> section for more information. === avg .variants * `avg(int64 n)` Calculate the running average of `n` between consecutive calls. ---- i: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. === clear .variants * `clear(map m)` *async* Clear all keys/values from map `m`. ---- i:ms:100 { @[rand % 10] = count(); } i:s:10 { print(@); clear(@); } ---- === count .variants * `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 this (PER_CPU), increasing performance. Due to this the map cannot be accessed as a regular integer. ---- i:ms:100 { @ = count(); } i:s:10 { print(@); clear(@); } ---- === delete .variants * `delete(mapkey k)` Delete a single key from a map. For a single value map this deletes the only element. For an associative-array the key to delete has to be specified. ``` k:dummy { @scalar = 1; @associative[1,2] = 1; delete(@scalar); delete(@associative[1,2]); delete(@associative); // error } ``` === hist .variants * `hist(int64 n)` Create a log2 histogram of `n`. ---- kretprobe:vfs_read { @bytes = hist(retval); } ---- Results in: ---- @: [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 .variants * `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`. ---- i:ms:1 { @ = lhist(rand %10, 0, 10, 1); } i: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 .variants * `max(int64 n)` Update the map with `n` if `n` is bigger than the current value held. === min .variants * `min(int64 n)` Update the map with `n` if `n` is smaller than the current value held. === stats .variants * `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 .variants * `sum(int64 n)` Calculate the sum of all `n` passed. === zero .variants * `zero(map m)` *async* Set all values for all keys to zero. == Functions Functions that are marked *async* are asynchronous which can lead to unexpected behaviour, see the <> section for more information. *compile time* functions are evaluated at compile time, a static value will be compiled into the program. *unsafe* functions can have dangerous side effects and should be used with care, the `--unsafe` flag is required for use. === buf .variants * `buf_t buf(void * data, [int64 length])` `buf` reads `length` amount of bytes from address `data`. The maximum value of `length` is limited to the `BPFTRACE_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 `buf_t` object returned by `buf` can safely be printed as a hex encoded string with the `%r` format specifier. Bytes with values >=32 and \<=126 are printed using their ASCII character, other bytes are printed in hex form (e.g. `\x00`). ---- i: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 .variants * `void cat(string namefmt, [...args])` *async* Dump the contents of the named file to stdout. `cat` supports the same format string and arguments that `printf` does. If the file cannot be opened or read an error is printed to stderr. ---- t: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 ---- === cgroupid .variants * `uint64 cgroupid(const string path)` *compile time* `cgroupid` retrieves the cgroupv2 ID of the cgroup available at `path`. ---- BEGIN { print(cgroupid("/sys/fs/cgroup/system.slice")); } ---- === exit .variants * `void exit()` *async* Terminate bpftrace, as if a `SIGTERM` was received. The `END` probe will still trigger (if specified) and maps will be printed. === join .variants * `void join(char *arr[], [char * sep = ' '])` *async* `join` joins all the string array `arr` with `sep` as separator into one string. This string will be printed to stdout directly, it cannot be used as string value. The concatenation of the array members is done in BPF and the printing happens in userspace. ---- tracepoint:syscalls:sys_enter_execve { join(args->argv); } ---- === kaddr .variants * `uint64 kaddr(const string name)` *compile time* Get the address of the kernel symbol `name`. The following script: === kptr .variants * `T * kptr(T * ptr)` Marks `ptr` as a kernel address space pointer. See the address-spaces section for more information on address-spaces. The pointer type is left unchanged. === ksym .variants * `ksym_t ksym(uint64 addr)` *async* Retrieve the name of the function that contains address `addr`. The address to name mapping happens in user-space. The `ksym_t` type can be printed with the `%s` format specifier. ---- kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); } ---- Prints: ---- do_nanosleep ---- === macaddr .variants * `macaddr_t macaddr(char [6] mac)` Create a buffer that holds a macaddress as read from `mac` This buffer can be printed in the canonical string format using the `%s` format specifier. ---- kprobe:arp_create { printf("SRC %s, DST %s\n", macaddr(sarg0), macaddr(sarg1)); } ---- Prints: ---- SRC 18:C0:4D:08:2E:BB, DST 74:83:C2:7F:8C:FF ---- === ntop .variants * `inet_t ntop([int64 af, ] int addr)` * `inet_t ntop([int64 af, ] char addr[4])` * `inet_t 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. === override .variants * `override(uint64 rc)` *unsafe* *Kernel* 4.16 *Helper* `bpf_override` .Supported probes * kprobe When using `override` the probed function will not be executed and instead `rc` will be returned. ---- k:__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' ---- === reg .variants * `reg(const string name)` .Supported probes * kprobe * uprobe Get the contents of the register identified by `name`. Valid names depend on the CPU architecture. === signal .variants * `signal(const string sig)` * `signal(uint32 signum)` *unsafe* *Kernel* 5.3 *Helper* `bpf_send_signal` Probe types: k(ret)probe, u(ret)probe, USDT, profile Send a signal to the process being traced. The signal can either be identified by name, e.g. `SIGSTOP` or by ID, e.g. `19` as found in `kill -l`. ---- kprobe:__x64_sys_execve /comm == "bash"/ { signal(5); } ---- ---- $ ls Trace/breakpoint trap (core dumped) ---- === sizeof .variants * `sizeof(TYPE)` * `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. === str .variants * `str(char * data [, uint32 length)` *Helper* `probe_read_str, probe_read_{kernel,user}_str` `str` reads a NULL terminated (`\0`) string from `data`. The maximum string length is limited by the `BPFTRACE_STR_LEN` env variable, unless `length` is specified and shorter than the maximum. In case the string is longer than the specified length only `length - 1` bytes are copied and a NULL byte is appended at the end. When available (starting from kernel 5.5, see the `--info` flag) bpftrace will automatically use the `kernel` or `user` variant of `probe_read_{kernel,user}_str` based on the address space of `data`, see <> for more information. === strftime .variants * `strtime_t 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 `timestr_t` 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. ---- i:s:1 { printf("%s\n", strftime("%H:%M:%S", nsecs)); } ---- bpftrace also supports the following format string extensions: [%header,cols="~,~"] |=== | Specifier | Description | `%f` | Microsecond as a decimal number, zero-padded on the left |=== === strncmp .variants * `int64 strncmp(char * s1, char * s2, int64 n)` `strncmp` compares up to `n` characters string `s1` and string `s2`. If they're equal `0` is returned, else a non-zero value is returned. bpftrace doesn't read past the length of the shortest string. The use of the `==` and `!=` operators is recommended over calling `strncmp` directly. === system .variants * `void system(string namefmt [, ...args])` *unsafe* *async* `system` lets bpftrace run the specified command (`fork` and `exec`) until it completes and print its stdout. The `command` is run with the same privileges as bpftrace and it blocks execution of the processing threads which can lead to missed events and delays processing of async events. ---- i:s:1 { time("%H:%M:%S: "); printf("%d\n", @++); } i:s:10 { system("/bin/sleep 10"); } i:s:30 { exit(); } ---- Note how the async `time` and `printf` first print every second until the `i:s:10` probe hits, then they print every 10 seconds due to bpftrace blocking on `sleep`. ---- Attaching 3 probes... 08:50:37: 0 08:50:38: 1 08:50:39: 2 08:50:40: 3 08:50:41: 4 08:50:42: 5 08:50:43: 6 08:50:44: 7 08:50:45: 8 08:50:46: 9 08:50:56: 10 08:50:56: 11 08:50:56: 12 08:50:56: 13 08:50:56: 14 08:50:56: 15 08:50:56: 16 08:50:56: 17 08:50:56: 18 08:50:56: 19 ---- `system` supports the same format string and arguments that `printf` does. ---- t:syscalls:sys_enter_execve { system("/bin/grep %s /proc/%d/status", "vmswap", pid); } ---- === time .variants * `void time(const string fmt)` *async* Format the current wall time according to the format specifier `fmt` and print it to stdout. Unlike `strftime()` `time()` doesn't send a timestamp from the probe, instead it is the time at which user-space processes the event. bpftrace uses the `strftime(3)` function for formatting time and supports the same format specifiers. === uaddr .variants * `T * uaddr(const string sym)` .Supported probes * uprobes * uretprobes * USDT **Does not work with ASLR, see issue link:https://github.com/iovisor/bpftrace/issues/75[#75]** The `uaddr` function returns the address of the specified symbol. This lookup happens during program compilation and cannot be used dynamically. The default return type is `uint64*`. If the ELF object size matches a known integer size (1, 2, 4 or 8 bytes) the return type is modified to match the width (`uint8*`, `uint16*`, `uint32*` or `uint64*` resp.). As ELF does not contain type info the type is always assumed to be unsigned. ---- uprobe:/bin/bash:readline { printf("PS1: %s\n", str(*uaddr("ps1_prompt"))); } ---- === uptr .variants * `T * uptr(T * ptr)` Marks `ptr` as a user address space pointer. See the address-spaces section for more information on address-spaces. The pointer type is left unchanged. === usym .variants * `usym_t usym(uint64 * addr)` *async* .Supported probes * uprobes * uretprobes Equal to <> but resolves user space symbols ---- uprobe:/bin/bash:readline { printf("%s\n", usym(reg("ip"))); } ---- Prints: ---- readline ---- === path .variants * `char * path(struct path * path)` *Kernel* 5.10 *Helper* bpf_d_path Return full path referenced by struct path pointer in argument. 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. === unwatch .variants * `void unwatch(void * addr)` *async* Removes a watchpoint == Output formatting === print .variants * `void print(T val)` *async* .variants * `void print(T val)` * `void print(@map)` * `void print(@map, uint64 top)` * `void print(@map, uint64 top, uint64 div)` `print` prints a the value, which can be a map or a scalar value, with the default formatting for the type. ---- i:ms:10 { @=hist(rand); } i:s:1 { print(@); print(123); print("abc"); 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 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| 123 abc ---- 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() } ---- ---- @[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: ---- k: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 ---- === printf .variants * `void printf(const string fmt, args...)` *async* `printf()` formats and prints data. It behaves similar to `printf()` found in `C` and many other languages. The format string has to be a constant, it cannot be modified at runtime. The formatting of the string happens in user space. Values are copied and passed by value. bpftrace supports all the typical format specifiers like `%llx` and `%hhu`. The non-standard ones can be found in the table below: [%header,cols="~,~,~"] |=== | Specifier | Type | Description | r | buffer | Hex-formatted string to print arbitrary binary content returned by the buf (<>) function. |=== Supported escape sequences Colors are supported too, using standard terminal escape sequences: ---- print("\033[31mRed\t\033[33mYellow\033[0m\n") ---- //// ! ! ! This prefix reset must be at the end of the section, else cross references break ! ! //// :idprefix: _ == Probes bpftrace supports various probe types which allow the user to attach BPF programs to different types of events. Each probe starts with a provider (e.g. `kprobe`) followed by a colon (`:`) separated list of options. The amount of options and their meaning depend on the provider and are detailed below. The valid values for options can depend on the system or binary being traced, e.g. for uprobes it depends on the binary. Also see <> It is possible to associate multiple probes with a single action as long as the action is valid for all specified probes. Multiple probes can be specified as a comma (`,`) separated list: ---- kprobe:tcp_reset,kprobe:tcp_v4_rcv { printf("Entered: %s\n", probe); } ---- Wildcards are supported too: ---- kprobe:tcp_* { printf("Entered: %s\n", probe); } ---- Both can be combined: ---- kprobe:tcp_reset,kprobe:*socket* { printf("Entered: %s\n", probe); } ---- Most providers also support a short name which can be used instead of the full name, e.g. `kprobe:f` and `k:f` are identical. [#probes-begin-end] === BEGIN and 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 the printing all used maps need be cleared, which can be done in the `END` probe: ---- END { clear(@map1); clear(@map2); } ---- [#probes-hardware] === hardware .variants * `hardware:event_name:` * `hardware:event_name:count` .shortname * `h` The `hardware` probe attaches to pre-defined hardware events provided by the kernel. 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. If `count` is left unspecified a default value is used. ---- hardware:cache-misses:1e6 { @[pid] = count(); } ---- [#probes-interval] === interval .variants * `interval:us:count` * `interval:ms:count` * `interval:s:count` * `interval:hz:rate` .shortnames * `i` The interval probe fires at a fixed interval as specified by its time spec. Interval fire on one CPU at the time, unlike <> probes. [#probes-iterator] === iterator .variants * `iter:task` * `iter:task:pin` * `iter:task_file` * `iter:task_file:pin` .shortnames * `it` These are eBPF iterator probes, that allow iteration over kernel objects. Iterator probe can't be mixed with any other probe, not even other iterator. Each iterator probe provides set of fields that could be accessed with ctx pointer. User can display set of available fields for iterator via -lv options as described below. Examples: ``` # bpftrace -e 'iter:task { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }' Attaching 1 probe... systemd:1 kthreadd:2 rcu_gp:3 rcu_par_gp:4 kworker/0:0H:6 mm_percpu_wq:8 ... # bpftrace -e 'iter:task_file { printf("%s:%d %d:%s\n", ctx->task->comm, ctx->task->pid, ctx->fd, path(ctx->file->f_path)); }' Attaching 1 probe... systemd:1 1:/dev/null systemd:1 2:/dev/null systemd:1 3:/dev/kmsg ... su:1622 1:/dev/pts/1 su:1622 2:/dev/pts/1 su:1622 3:/var/lib/sss/mc/passwd ... bpftrace:1892 1:pipe:[35124] bpftrace:1892 2:/dev/pts/1 bpftrace:1892 3:anon_inode:bpf-map bpftrace:1892 4:anon_inode:bpf-map bpftrace:1892 5:anon_inode:bpf_link bpftrace:1892 6:anon_inode:bpf-prog bpftrace:1892 7:anon_inode:bpf_iter ``` It's possible to pin iterator with specifying optional probe ':pin' part, that defines the pin file. It can be specified as absolute path or relative to /sys/fs/bpf. .relative pin ---- # bpftrace -e 'iter:task:list { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }' Program pinned to /sys/fs/bpf/list ---- ---- # cat /sys/fs/bpf/list systemd:1 kthreadd:2 rcu_gp:3 rcu_par_gp:4 kworker/0:0H:6 mm_percpu_wq:8 rcu_tasks_kthre:9 ... ---- Examples with absolute pin file: .absolute pin ---- # bpftrace -e ' iter:task_file:/sys/fs/bpf/files { printf("%s:%d %s\n", ctx->task->comm, ctx->task->pid, path(ctx->file->f_path)); }' Program pinned to /sys/fs/bpf/files ---- ---- # cat /sys/fs/bpf/files systemd:1 anon_inode:inotify systemd:1 anon_inode:[timerfd] ... systemd-journal:849 /dev/kmsg systemd-journal:849 anon_inode:[eventpoll] ... sssd:1146 /var/log/sssd/sssd.log sssd:1146 anon_inode:[eventpoll] ... NetworkManager:1155 anon_inode:[eventfd] NetworkManager:1155 /var/lib/sss/mc/passwd (deleted) ---- [#probes-kfunc] === kfunc and kretfunc .variants * `kfunc:fn` * `kretfunc:fn` .shortnames * `f` (`kfunc`) * `fr` (`kretfunc`) .requires (`--info`) * Kernel features:BTF * Probe types:kfunc ``kfunc``s attach to kernel function similar to <>. They make use of eBPF trampolines which allows kernel code to call into BPF programs with near zero overhead. `kfunc` s make use of BTF type information to derive the type of function arguments at compile time. This removes the need for manual type casting and makes the code more resilient against small signature changes in the kernel. The function arguments are available in the `args` struct which can be inspected by doing verbose listing (see <>). These arguments are also available in the return probe (`kretfunc`). ---- # bpftrace -lv 'kfunc:tcp_reset' kfunc:tcp_reset struct sock * sk struct sk_buff * skb ---- ---- kfunc:x86_pmu_stop { printf("pmu %s stop\n", str(args->event->pmu->name)); } ---- ---- kretfunc:fget { printf("fd %d name %s\n", args->fd, str(retval->f_path.dentry->d_name.name)); } ---- ---- fd 3 name ld.so.cache fd 3 name libselinux.so.1 fd 3 name libselinux.so.1 ... ---- [#probes-kprobe] === kprobe and kretprobe .variants * `kprobe:fn` * `kprobe:fn+offset` * `kretprobe:fn` .shortnames * `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 `argX` and `sargX` builtins, for register args and stack args respectively. Whether arguments passed on stack or in a register depends on the architecture and the number or arguments in used, e.g. on x86_64 the first non-floating point 6 arguments are passed in registers, all following arguments are passed on the stack. Note that floating point arguments are typically passed in special registers which don't count as `argX` arguments which can cause confusion. Consider a function with the following signature: ---- void func(int a, double d, int x) ---- Due to `d` being a floating point `x` is accessed through `arg1` where one might expect `arg2`. bpftrace does not detect the function signature so it is not aware of the argument count or their type. It is up to the user to perform <> when needed, e.g. ---- kprobe:tcp_connect { $sk = ((struct sock *) arg0); ... } ---- `kprobe` s are not limited to function entry, they can be attached to any instruction in a function by specifying an offset from the start of the function. `kretprobe` s trigger on the return from a kernel function. Return probes do not have access to the function (input) arguments, only to the return value (through `retval`). A common pattern to work around this is by storing the arguments in a map on function entry and retrieving in the return probe: ---- kprobe:d_lookup { $name = (struct qstr *)arg1; @fname[tid] = $name->name; } kretprobe:d_lookup /@fname[tid]/ { printf("%-8d %-6d %-16s M %s\n", elapsed / 1e6, pid, comm, str(@fname[tid])); } ---- [#probes-profile] === profile .variants * `profile:us:count` * `profile:ms:count` * `profile:s:count` * `profile:hz:rate` .shortnames * `p` Profile probes fire on each CPU on the specified interval. [#probes-software] === software .variants * `software:event:` * `software:event:count` .shortnames * `s` The `software` probe attaches to pre-defined software events provided by the kernel. Event details can be found in the `perf_event_open(2)` man page. 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` [#probes-tracepoint] === tracepoint .variants * `tracepoint:subsys:event` .shortnames * `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. Which means that 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 arguments are available in the `args` struct which can be inspected with verbose listing, see the <> section for more details. ---- tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); } ---- ---- irqbalance /proc/interrupts irqbalance /proc/stat snmpd /proc/diskstats snmpd /proc/stat snmpd /proc/vmstat snmpd /proc/net/dev [...] ---- .Additional information * https://www.kernel.org/doc/html/latest/trace/tracepoints.html [#probes-uprobe] === uprobe, uretprobe .variants * `uprobe:binary:func` * `uprobe:binary:func+offset` * `uretprobe:binary:func` .shortnames * `u` * `ur` `uprobe` s or user-space probes are the user-space equivalent of `kprobe` s. The same limitations that apply <> also apply to `uprobe` s and `uretprobe` s. 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`: ---- # bpftrace -e 'uprobe:libc:malloc { printf("Allocated %d bytes\n", arg0); }' Allocated 4 bytes ... ---- If the traced binary has DWARF included, function arguments are available in the `args` struct which can be inspected with verbose listing, see the <> section for more details. It is important to note that for `uretprobe` s to work the kernel runs a special helper on user-space function entry which overrides the return address on the stack. This can cause issues with languages that have their own runtime like Golang: .example.go ---- func myprint(s string) { fmt.Printf("Input: %s\n", s) } func main() { ss := []string{"a", "b", "c"} for _, s := range ss { go myprint(s) } time.Sleep(1*time.Second) } ---- .bpftrace ---- # bpftrace -e 'uretprobe:./test:main.myprint { @=count(); }' -c ./test runtime: unexpected return pc for main.myprint called from 0x7fffffffe000 stack: frame={sp:0xc00008cf60, fp:0xc00008cfd0} stack=[0xc00008c000,0xc00008d000) fatal error: unknown caller pc ---- [#probes-usdt] === usdt .variants * `usdt:binary:name` .shortnames * `U` [#probes-watchpoint] === watchpoint and asyncwatchpoint .variants * `watchpoint:absolute_address:length:mode` * `watchpoint:function+argN:length:mode` .shortnames * `w` * `aw` 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. Examples Print `hit` when a read from or write to `0x10000000` happens: ``` # bpftrace -e 'watchpoint:0x10000000:8:rw { printf("hit!\n"); exit(); }' -c ./testprogs/watchpoint ``` Print the call stack every time the `jiffies` variable is updated: ``` # bpftrace -e "watchpoint:0x$(awk '$3 == "jiffies" {print $1}' /proc/kallsyms):8:w { @[kstack] = count(); } i:s:1 { exit(); }" ...... @[ do_timer+12 tick_do_update_jiffies64.part.22+89 tick_sched_do_timer+103 tick_sched_timer+39 __hrtimer_run_queues+256 hrtimer_interrupt+256 smp_apic_timer_interrupt+106 apic_timer_interrupt+15 cpuidle_enter_state+188 cpuidle_enter+41 do_idle+536 cpu_startup_entry+25 start_secondary+355 secondary_startup_64+164 ]: 319 ``` "hit" and exit when the memory pointed to by `arg1` of `increment` is written to. ``` # 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 ``` == 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: ---- # bpftrace -l 'kprobe:*' # bpftrace -l 't:syscalls:*openat* # bpftrace -l 'kprobe:tcp*,trace # bpftrace -l 'k:*socket*,tracepoint:syscalls:*tcp*' ---- The verbose flag (`-v`) can be specified to inspect arguments (`args`) for providers that support it: ---- # bpftrace -l 'fr:tcp_reset,t:syscalls:sys_enter_openat' -v kretfunc: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-0.14.0/man/man8/000077500000000000000000000000001413460502400147775ustar00rootroot00000000000000bpftrace-0.14.0/man/man8/CMakeLists.txt000066400000000000000000000007431413460502400175430ustar00rootroot00000000000000find_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} -c ${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.14.0/man/man8/bashreadline.8000066400000000000000000000025601413460502400175140ustar00rootroot00000000000000.TH bashreadline 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/iovisor/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(8) bpftrace-0.14.0/man/man8/biolatency.8000066400000000000000000000034121413460502400172210ustar00rootroot00000000000000.TH biolatency 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. 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 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/iovisor/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(1) bpftrace-0.14.0/man/man8/biosnoop.8000066400000000000000000000034241413460502400167230ustar00rootroot00000000000000.TH biosnoop 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/iovisor/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(8) bpftrace-0.14.0/man/man8/biostacks.8000066400000000000000000000036751413460502400170650ustar00rootroot00000000000000.TH biostacks 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 tracing the blk_account_io_start() and the blk_start_request() or blk_mq_start_request() functions using dynamic instrumentation. Linux 5.0 removed the classic I/O scheduler, so the blk_start_request() probe can be removed from the tool (just delete it). This tool may need other maintenance to keep working if these functions change in later kernels. 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/iovisor/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(8) bpftrace-0.14.0/man/man8/bitesize.8000066400000000000000000000027771413460502400167230ustar00rootroot00000000000000.TH bitesize 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(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/iovisor/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.14.0/man/man8/capable.8000066400000000000000000000027001413460502400164560ustar00rootroot00000000000000.TH capable 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/iovisor/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.14.0/man/man8/cpuwalk.8000066400000000000000000000023101413460502400165320ustar00rootroot00000000000000.TH cpuwalk 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/iovisor/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.14.0/man/man8/dcsnoop.8000066400000000000000000000040151413460502400165350ustar00rootroot00000000000000.TH dcsnoop 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/iovisor/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.14.0/man/man8/execsnoop.8000066400000000000000000000034661413460502400171040ustar00rootroot00000000000000.TH execsnoop 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 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/iovisor/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(8) bpftrace-0.14.0/man/man8/gethostlatency.8000066400000000000000000000033341413460502400201300ustar00rootroot00000000000000.TH gethostlatency 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/iovisor/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.14.0/man/man8/killsnoop.8000066400000000000000000000031321413460502400171010ustar00rootroot00000000000000.TH killsnoop 8 "2018-09-07" "USER COMMANDS" .SH NAME killsnoop.bt \- Trace signals issued by the kill() syscall. Uses bpftrace/eBPF. .SH SYNOPSIS .B killsnoop.bt .SH DESCRIPTION killsnoop traces the kill() syscall, to show signals sent via this method. This may be useful to troubleshoot failing applications, where an unknown mechanism is sending signals. This works by tracing the kill() syscall tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace all kill() syscalls: # .B killsnoop.bt .SH FIELDS .TP TIME Time of the kill call. .TP PID Source process ID .TP COMM Source process name .TP SIG Signal number. See signal(7). .TP TPID Target process ID .TP RES Result. 0 == success, a negative value (of the error code) for failure. .SH OVERHEAD This traces the kernel kill function and prints output for each event. As the rate of this is generally expected to be low (< 100/s), the overhead is also expected to be negligible. If you have an application that is calling a very high rate of kill()s for some reason, then test and understand overhead before use. .SH SOURCE This is from bpftrace. .IP https://github.com/iovisor/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(8) bpftrace-0.14.0/man/man8/loads.8000066400000000000000000000037341413460502400162010ustar00rootroot00000000000000.TH loads 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/iovisor/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.14.0/man/man8/mdflush.8000066400000000000000000000032131413460502400165310ustar00rootroot00000000000000.TH mdflush 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/iovisor/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(8) bpftrace-0.14.0/man/man8/naptime.8000066400000000000000000000027731413460502400165360ustar00rootroot00000000000000.TH naptime 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/iovisor/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(8) bpftrace-0.14.0/man/man8/oomkill.8000066400000000000000000000034141413460502400165400ustar00rootroot00000000000000.TH oomkill 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/iovisor/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.14.0/man/man8/opensnoop.8000066400000000000000000000031001413460502400171020ustar00rootroot00000000000000.TH opensnoop 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/iovisor/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(8), execsnoop(8) bpftrace-0.14.0/man/man8/pidpersec.8000066400000000000000000000026551413460502400170560ustar00rootroot00000000000000.TH pidpersec 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/iovisor/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.14.0/man/man8/runqlat.8000066400000000000000000000034231413460502400165600ustar00rootroot00000000000000.TH runqlat 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/iovisor/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(8), mpstat(1), pidstat(1), uptime(1) bpftrace-0.14.0/man/man8/runqlen.8000066400000000000000000000030731413460502400165570ustar00rootroot00000000000000.TH runqlen 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/iovisor/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(8), mpstat(1), pidstat(1), uptime(1) bpftrace-0.14.0/man/man8/setuids.8000066400000000000000000000027741413460502400165620ustar00rootroot00000000000000.TH setuids 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/iovisor/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(8) bpftrace-0.14.0/man/man8/statsnoop.8000066400000000000000000000033031413460502400171210ustar00rootroot00000000000000.TH statsnoop 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/iovisor/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(8), execsnoop(8) bpftrace-0.14.0/man/man8/swapin.8000066400000000000000000000030421413460502400163700ustar00rootroot00000000000000.TH swapin 8 "2019-07-05" "USER COMMANDS" .SH NAME swapin \- 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/iovisor/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.14.0/man/man8/syncsnoop.8000066400000000000000000000030531413460502400171240ustar00rootroot00000000000000.TH syncsnoop 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/iovisor/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.14.0/man/man8/syscount.8000066400000000000000000000044471413460502400167700ustar00rootroot00000000000000.TH syscount 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 all VFS 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/iovisor/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.14.0/man/man8/tcpaccept.8000066400000000000000000000041421413460502400170370ustar00rootroot00000000000000.TH tcpaccept 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/iovisor/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(8), funccount(8), tcpdump(8) bpftrace-0.14.0/man/man8/tcpconnect.8000066400000000000000000000035751413460502400172420ustar00rootroot00000000000000.TH tcpconnect 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/iovisor/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(8), funccount(8), tcpdump(8) bpftrace-0.14.0/man/man8/tcpdrop.8000066400000000000000000000040671413460502400165520ustar00rootroot00000000000000.TH tcpdrop 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 Destionation 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/iovisor/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(8), tcpaccept(8), tcpconnect(8), tcptop(8) bpftrace-0.14.0/man/man8/tcplife.8000066400000000000000000000043771413460502400165310ustar00rootroot00000000000000.TH tcplife 8 "2019-07-03" "USER COMMANDS" .SH NAME tcplife \- 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/iovisor/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.14.0/man/man8/tcpretrans.8000066400000000000000000000037531413460502400172650ustar00rootroot00000000000000.TH tcpretrans 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/iovisor/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(8), tcpaccept(8) bpftrace-0.14.0/man/man8/tcpsynbl.8000066400000000000000000000034721413460502400167340ustar00rootroot00000000000000.TH tcpsynbl 8 "2019-07-03" "USER COMMANDS" .SH NAME tcpsynbl \- 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/iovisor/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.14.0/man/man8/threadsnoop.8000066400000000000000000000031711413460502400174200ustar00rootroot00000000000000.TH threadsnoop 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(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/iovisor/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(8) bpftrace-0.14.0/man/man8/vfscount.8000066400000000000000000000032251413460502400167410ustar00rootroot00000000000000.TH vfscount 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/iovisor/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.14.0/man/man8/vfsstat.8000066400000000000000000000034311413460502400165630ustar00rootroot00000000000000.TH vfsstat 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/iovisor/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.14.0/man/man8/writeback.8000066400000000000000000000033121413460502400170420ustar00rootroot00000000000000.TH writeback 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/iovisor/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(8) bpftrace-0.14.0/man/man8/xfsdist.8000066400000000000000000000034301413460502400165540ustar00rootroot00000000000000.TH xfsdist 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/iovisor/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(8) bpftrace-0.14.0/resources/000077500000000000000000000000001413460502400153735ustar00rootroot00000000000000bpftrace-0.14.0/resources/CMakeLists.txt000066400000000000000000000012351413460502400201340ustar00rootroot00000000000000add_library(resources headers.cpp) target_include_directories(resources PUBLIC ../src) function(embed_headers output) file(WRITE ${output} "#include \"headers.h\"\n\nnamespace bpftrace {\n") file(GLOB headers *.h) foreach(header ${headers}) get_filename_component(filename ${header} NAME) string(MAKE_C_IDENTIFIER ${filename} varname) file(READ ${header} contents) file(APPEND ${output} "const char ${varname}[] = R\"CONTENTS(${contents})CONTENTS\";\nconst unsigned ${varname}_len = sizeof(${varname});\n") endforeach() file(APPEND ${output} "} // namespace bpftrace") endfunction() embed_headers(${CMAKE_BINARY_DIR}/resources/headers.cpp) bpftrace-0.14.0/resources/__stddef_max_align_t.h000066400000000000000000000033521413460502400216600ustar00rootroot00000000000000/*===---- __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.14.0/resources/clang_workarounds.h000066400000000000000000000016301413460502400212660ustar00rootroot00000000000000#ifndef __CLANG_WORKAROUNDS_H #define __CLANG_WORKAROUNDS_H // linux/types.h is included by default, which will bring // in asm_volatile_goto definition if permitted based on // compiler setup and kernel configs. // // clang does not support "asm volatile goto" yet. // So redefine asm_volatile_goto to some invalid asm code. // We won't execute this code anyway, so we just need to make sure clang is // able to parse our headers. // // From: https://github.com/iovisor/bcc/pull/2133/files #include #ifdef asm_volatile_goto #undef asm_volatile_goto #define asm_volatile_goto(x...) asm volatile("invalid use of asm_volatile_goto") #endif // In Linux 5.4 asm_inline was introduced, but it's not supported by clang. // Redefine it to just asm to enable successful compilation. // // From: https://github.com/iovisor/bcc/pull/2547 #ifdef asm_inline #undef asm_inline #define asm_inline asm #endif #endif bpftrace-0.14.0/resources/float.h000066400000000000000000000121071413460502400166520ustar00rootroot00000000000000/*===---- 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.14.0/resources/limits.h000066400000000000000000000072261413460502400170540ustar00rootroot00000000000000/*===---- 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.14.0/resources/stdarg.h000066400000000000000000000037501413460502400170350ustar00rootroot00000000000000/*===---- stdarg.h - Variable argument handling ----------------------------=== * * Copyright (c) 2008 Eli Friedman * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * *===-----------------------------------------------------------------------=== */ #ifndef __STDARG_H #define __STDARG_H #ifndef _VA_LIST typedef __builtin_va_list va_list; #define _VA_LIST #endif #define va_start(ap, param) __builtin_va_start(ap, param) #define va_end(ap) __builtin_va_end(ap) #define va_arg(ap, type) __builtin_va_arg(ap, type) /* GCC always defines __va_copy, but does not define va_copy unless in c99 mode * or -ansi is not specified, since it was not part of C90. */ #define __va_copy(d,s) __builtin_va_copy(d,s) #if __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L || !defined(__STRICT_ANSI__) #define va_copy(dest, src) __builtin_va_copy(dest, src) #endif #ifndef __GNUC_VA_LIST #define __GNUC_VA_LIST 1 typedef __builtin_va_list __gnuc_va_list; #endif #endif /* __STDARG_H */ bpftrace-0.14.0/resources/stddef.h000066400000000000000000000106221413460502400170160ustar00rootroot00000000000000/*===---- stddef.h - Basic type definitions --------------------------------=== * * Copyright (c) 2008 Eli Friedman * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * *===-----------------------------------------------------------------------=== */ #if !defined(__STDDEF_H) || defined(__need_ptrdiff_t) || \ defined(__need_size_t) || defined(__need_wchar_t) || \ defined(__need_NULL) || defined(__need_wint_t) #if !defined(__need_ptrdiff_t) && !defined(__need_size_t) && \ !defined(__need_wchar_t) && !defined(__need_NULL) && \ !defined(__need_wint_t) /* Always define miscellaneous pieces when modules are available. */ #if !__has_feature(modules) #define __STDDEF_H #endif #define __need_ptrdiff_t #define __need_size_t #define __need_wchar_t #define __need_NULL #define __need_STDDEF_H_misc /* __need_wint_t is intentionally not defined here. */ #endif #if defined(__need_ptrdiff_t) #if !defined(_PTRDIFF_T) || __has_feature(modules) /* Always define ptrdiff_t when modules are available. */ #if !__has_feature(modules) #define _PTRDIFF_T #endif typedef __PTRDIFF_TYPE__ ptrdiff_t; #endif #undef __need_ptrdiff_t #endif /* defined(__need_ptrdiff_t) */ #if defined(__need_size_t) #if !defined(_SIZE_T) || __has_feature(modules) /* Always define size_t when modules are available. */ #if !__has_feature(modules) #define _SIZE_T #endif typedef __SIZE_TYPE__ size_t; #endif #undef __need_size_t #endif /*defined(__need_size_t) */ #if defined(__need_STDDEF_H_misc) /* ISO9899:2011 7.20 (C11 Annex K): Define rsize_t if __STDC_WANT_LIB_EXT1__ is * enabled. */ #if (defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1 && \ !defined(_RSIZE_T)) || __has_feature(modules) /* Always define rsize_t when modules are available. */ #if !__has_feature(modules) #define _RSIZE_T #endif typedef __SIZE_TYPE__ rsize_t; #endif #endif /* defined(__need_STDDEF_H_misc) */ #if defined(__need_wchar_t) #ifndef __cplusplus /* Always define wchar_t when modules are available. */ #if !defined(_WCHAR_T) || __has_feature(modules) #if !__has_feature(modules) #define _WCHAR_T #if defined(_MSC_EXTENSIONS) #define _WCHAR_T_DEFINED #endif #endif typedef __WCHAR_TYPE__ wchar_t; #endif #endif #undef __need_wchar_t #endif /* defined(__need_wchar_t) */ #if defined(__need_NULL) #undef NULL #ifdef __cplusplus # if !defined(__MINGW32__) && !defined(_MSC_VER) # define NULL __null # else # define NULL 0 # endif #else # define NULL ((void*)0) #endif #ifdef __cplusplus #if defined(_MSC_EXTENSIONS) && defined(_NATIVE_NULLPTR_SUPPORTED) namespace std { typedef decltype(nullptr) nullptr_t; } using ::std::nullptr_t; #endif #endif #undef __need_NULL #endif /* defined(__need_NULL) */ #if defined(__need_STDDEF_H_misc) #if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L #include "__stddef_max_align_t.h" #endif #define offsetof(t, d) __builtin_offsetof(t, d) #undef __need_STDDEF_H_misc #endif /* defined(__need_STDDEF_H_misc) */ /* Some C libraries expect to see a wint_t here. Others (notably MinGW) will use __WINT_TYPE__ directly; accommodate both by requiring __need_wint_t */ #if defined(__need_wint_t) /* Always define wint_t when modules are available. */ #if !defined(_WINT_T) || __has_feature(modules) #if !__has_feature(modules) #define _WINT_T #endif typedef __WINT_TYPE__ wint_t; #endif #undef __need_wint_t #endif /* __need_wint_t */ #endif bpftrace-0.14.0/resources/stdint.h000066400000000000000000000555351413460502400170660ustar00rootroot00000000000000/*===---- stdint.h - Standard header for sized integer types --------------===*\ * * Copyright (c) 2009 Chris Lattner * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * \*===----------------------------------------------------------------------===*/ #ifndef __CLANG_STDINT_H #define __CLANG_STDINT_H /* If we're hosted, fall back to the system's stdint.h, which might have * additional definitions. */ #if __STDC_HOSTED__ && __has_include_next() // C99 7.18.3 Limits of other integer types // // Footnote 219, 220: C++ implementations should define these macros only when // __STDC_LIMIT_MACROS is defined before is included. // // Footnote 222: C++ implementations should define these macros only when // __STDC_CONSTANT_MACROS is defined before is included. // // C++11 [cstdint.syn]p2: // // The macros defined by are provided unconditionally. In particular, // the symbols __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS (mentioned in // footnotes 219, 220, and 222 in the C standard) play no role in C++. // // C11 removed the problematic footnotes. // // Work around this inconsistency by always defining those macros in C++ mode, // so that a C library implementation which follows the C99 standard can be // used in C++. # ifdef __cplusplus # if !defined(__STDC_LIMIT_MACROS) # define __STDC_LIMIT_MACROS # define __STDC_LIMIT_MACROS_DEFINED_BY_CLANG # endif # if !defined(__STDC_CONSTANT_MACROS) # define __STDC_CONSTANT_MACROS # define __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG # endif # endif # include_next # ifdef __STDC_LIMIT_MACROS_DEFINED_BY_CLANG # undef __STDC_LIMIT_MACROS # undef __STDC_LIMIT_MACROS_DEFINED_BY_CLANG # endif # ifdef __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG # undef __STDC_CONSTANT_MACROS # undef __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG # endif #else /* C99 7.18.1.1 Exact-width integer types. * C99 7.18.1.2 Minimum-width integer types. * C99 7.18.1.3 Fastest minimum-width integer types. * * The standard requires that exact-width type be defined for 8-, 16-, 32-, and * 64-bit types if they are implemented. Other exact width types are optional. * This implementation defines an exact-width types for every integer width * that is represented in the standard integer types. * * The standard also requires minimum-width types be defined for 8-, 16-, 32-, * and 64-bit widths regardless of whether there are corresponding exact-width * types. * * To accommodate targets that are missing types that are exactly 8, 16, 32, or * 64 bits wide, this implementation takes an approach of cascading * redefinitions, redefining __int_leastN_t to successively smaller exact-width * types. It is therefore important that the types are defined in order of * descending widths. * * We currently assume that the minimum-width types and the fastest * minimum-width types are the same. This is allowed by the standard, but is * suboptimal. * * In violation of the standard, some targets do not implement a type that is * wide enough to represent all of the required widths (8-, 16-, 32-, 64-bit). * To accommodate these targets, a required minimum-width type is only * defined if there exists an exact-width type of equal or greater width. */ #ifdef __INT64_TYPE__ # ifndef __int8_t_defined /* glibc sys/types.h also defines int64_t*/ typedef __INT64_TYPE__ int64_t; # endif /* __int8_t_defined */ typedef __UINT64_TYPE__ uint64_t; # define __int_least64_t int64_t # define __uint_least64_t uint64_t # define __int_least32_t int64_t # define __uint_least32_t uint64_t # define __int_least16_t int64_t # define __uint_least16_t uint64_t # define __int_least8_t int64_t # define __uint_least8_t uint64_t #endif /* __INT64_TYPE__ */ #ifdef __int_least64_t typedef __int_least64_t int_least64_t; typedef __uint_least64_t uint_least64_t; typedef __int_least64_t int_fast64_t; typedef __uint_least64_t uint_fast64_t; #endif /* __int_least64_t */ #ifdef __INT56_TYPE__ typedef __INT56_TYPE__ int56_t; typedef __UINT56_TYPE__ uint56_t; typedef int56_t int_least56_t; typedef uint56_t uint_least56_t; typedef int56_t int_fast56_t; typedef uint56_t uint_fast56_t; # define __int_least32_t int56_t # define __uint_least32_t uint56_t # define __int_least16_t int56_t # define __uint_least16_t uint56_t # define __int_least8_t int56_t # define __uint_least8_t uint56_t #endif /* __INT56_TYPE__ */ #ifdef __INT48_TYPE__ typedef __INT48_TYPE__ int48_t; typedef __UINT48_TYPE__ uint48_t; typedef int48_t int_least48_t; typedef uint48_t uint_least48_t; typedef int48_t int_fast48_t; typedef uint48_t uint_fast48_t; # define __int_least32_t int48_t # define __uint_least32_t uint48_t # define __int_least16_t int48_t # define __uint_least16_t uint48_t # define __int_least8_t int48_t # define __uint_least8_t uint48_t #endif /* __INT48_TYPE__ */ #ifdef __INT40_TYPE__ typedef __INT40_TYPE__ int40_t; typedef __UINT40_TYPE__ uint40_t; typedef int40_t int_least40_t; typedef uint40_t uint_least40_t; typedef int40_t int_fast40_t; typedef uint40_t uint_fast40_t; # define __int_least32_t int40_t # define __uint_least32_t uint40_t # define __int_least16_t int40_t # define __uint_least16_t uint40_t # define __int_least8_t int40_t # define __uint_least8_t uint40_t #endif /* __INT40_TYPE__ */ #ifdef __INT32_TYPE__ # ifndef __int8_t_defined /* glibc sys/types.h also defines int32_t*/ typedef __INT32_TYPE__ int32_t; # endif /* __int8_t_defined */ # ifndef __uint32_t_defined /* more glibc compatibility */ # define __uint32_t_defined typedef __UINT32_TYPE__ uint32_t; # endif /* __uint32_t_defined */ # define __int_least32_t int32_t # define __uint_least32_t uint32_t # define __int_least16_t int32_t # define __uint_least16_t uint32_t # define __int_least8_t int32_t # define __uint_least8_t uint32_t #endif /* __INT32_TYPE__ */ #ifdef __int_least32_t typedef __int_least32_t int_least32_t; typedef __uint_least32_t uint_least32_t; typedef __int_least32_t int_fast32_t; typedef __uint_least32_t uint_fast32_t; #endif /* __int_least32_t */ #ifdef __INT24_TYPE__ typedef __INT24_TYPE__ int24_t; typedef __UINT24_TYPE__ uint24_t; typedef int24_t int_least24_t; typedef uint24_t uint_least24_t; typedef int24_t int_fast24_t; typedef uint24_t uint_fast24_t; # define __int_least16_t int24_t # define __uint_least16_t uint24_t # define __int_least8_t int24_t # define __uint_least8_t uint24_t #endif /* __INT24_TYPE__ */ #ifdef __INT16_TYPE__ #ifndef __int8_t_defined /* glibc sys/types.h also defines int16_t*/ typedef __INT16_TYPE__ int16_t; #endif /* __int8_t_defined */ typedef __UINT16_TYPE__ uint16_t; # define __int_least16_t int16_t # define __uint_least16_t uint16_t # define __int_least8_t int16_t # define __uint_least8_t uint16_t #endif /* __INT16_TYPE__ */ #ifdef __int_least16_t typedef __int_least16_t int_least16_t; typedef __uint_least16_t uint_least16_t; typedef __int_least16_t int_fast16_t; typedef __uint_least16_t uint_fast16_t; #endif /* __int_least16_t */ #ifdef __INT8_TYPE__ #ifndef __int8_t_defined /* glibc sys/types.h also defines int8_t*/ typedef __INT8_TYPE__ int8_t; #endif /* __int8_t_defined */ typedef __UINT8_TYPE__ uint8_t; # define __int_least8_t int8_t # define __uint_least8_t uint8_t #endif /* __INT8_TYPE__ */ #ifdef __int_least8_t typedef __int_least8_t int_least8_t; typedef __uint_least8_t uint_least8_t; typedef __int_least8_t int_fast8_t; typedef __uint_least8_t uint_fast8_t; #endif /* __int_least8_t */ /* prevent glibc sys/types.h from defining conflicting types */ #ifndef __int8_t_defined # define __int8_t_defined #endif /* __int8_t_defined */ /* C99 7.18.1.4 Integer types capable of holding object pointers. */ #define __stdint_join3(a,b,c) a ## b ## c #ifndef _INTPTR_T #ifndef __intptr_t_defined typedef __INTPTR_TYPE__ intptr_t; #define __intptr_t_defined #define _INTPTR_T #endif #endif #ifndef _UINTPTR_T typedef __UINTPTR_TYPE__ uintptr_t; #define _UINTPTR_T #endif /* C99 7.18.1.5 Greatest-width integer types. */ typedef __INTMAX_TYPE__ intmax_t; typedef __UINTMAX_TYPE__ uintmax_t; /* C99 7.18.4 Macros for minimum-width integer constants. * * The standard requires that integer constant macros be defined for all the * minimum-width types defined above. As 8-, 16-, 32-, and 64-bit minimum-width * types are required, the corresponding integer constant macros are defined * here. This implementation also defines minimum-width types for every other * integer width that the target implements, so corresponding macros are * defined below, too. * * These macros are defined using the same successive-shrinking approach as * the type definitions above. It is likewise important that macros are defined * in order of decending width. * * Note that C++ should not check __STDC_CONSTANT_MACROS here, contrary to the * claims of the C standard (see C++ 18.3.1p2, [cstdint.syn]). */ #define __int_c_join(a, b) a ## b #define __int_c(v, suffix) __int_c_join(v, suffix) #define __uint_c(v, suffix) __int_c_join(v##U, suffix) #ifdef __INT64_TYPE__ # ifdef __INT64_C_SUFFIX__ # define __int64_c_suffix __INT64_C_SUFFIX__ # define __int32_c_suffix __INT64_C_SUFFIX__ # define __int16_c_suffix __INT64_C_SUFFIX__ # define __int8_c_suffix __INT64_C_SUFFIX__ # else # undef __int64_c_suffix # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT64_C_SUFFIX__ */ #endif /* __INT64_TYPE__ */ #ifdef __int_least64_t # ifdef __int64_c_suffix # define INT64_C(v) __int_c(v, __int64_c_suffix) # define UINT64_C(v) __uint_c(v, __int64_c_suffix) # else # define INT64_C(v) v # define UINT64_C(v) v ## U # endif /* __int64_c_suffix */ #endif /* __int_least64_t */ #ifdef __INT56_TYPE__ # ifdef __INT56_C_SUFFIX__ # define INT56_C(v) __int_c(v, __INT56_C_SUFFIX__) # define UINT56_C(v) __uint_c(v, __INT56_C_SUFFIX__) # define __int32_c_suffix __INT56_C_SUFFIX__ # define __int16_c_suffix __INT56_C_SUFFIX__ # define __int8_c_suffix __INT56_C_SUFFIX__ # else # define INT56_C(v) v # define UINT56_C(v) v ## U # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT56_C_SUFFIX__ */ #endif /* __INT56_TYPE__ */ #ifdef __INT48_TYPE__ # ifdef __INT48_C_SUFFIX__ # define INT48_C(v) __int_c(v, __INT48_C_SUFFIX__) # define UINT48_C(v) __uint_c(v, __INT48_C_SUFFIX__) # define __int32_c_suffix __INT48_C_SUFFIX__ # define __int16_c_suffix __INT48_C_SUFFIX__ # define __int8_c_suffix __INT48_C_SUFFIX__ # else # define INT48_C(v) v # define UINT48_C(v) v ## U # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT48_C_SUFFIX__ */ #endif /* __INT48_TYPE__ */ #ifdef __INT40_TYPE__ # ifdef __INT40_C_SUFFIX__ # define INT40_C(v) __int_c(v, __INT40_C_SUFFIX__) # define UINT40_C(v) __uint_c(v, __INT40_C_SUFFIX__) # define __int32_c_suffix __INT40_C_SUFFIX__ # define __int16_c_suffix __INT40_C_SUFFIX__ # define __int8_c_suffix __INT40_C_SUFFIX__ # else # define INT40_C(v) v # define UINT40_C(v) v ## U # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT40_C_SUFFIX__ */ #endif /* __INT40_TYPE__ */ #ifdef __INT32_TYPE__ # ifdef __INT32_C_SUFFIX__ # define __int32_c_suffix __INT32_C_SUFFIX__ # define __int16_c_suffix __INT32_C_SUFFIX__ # define __int8_c_suffix __INT32_C_SUFFIX__ #else # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT32_C_SUFFIX__ */ #endif /* __INT32_TYPE__ */ #ifdef __int_least32_t # ifdef __int32_c_suffix # define INT32_C(v) __int_c(v, __int32_c_suffix) # define UINT32_C(v) __uint_c(v, __int32_c_suffix) # else # define INT32_C(v) v # define UINT32_C(v) v ## U # endif /* __int32_c_suffix */ #endif /* __int_least32_t */ #ifdef __INT24_TYPE__ # ifdef __INT24_C_SUFFIX__ # define INT24_C(v) __int_c(v, __INT24_C_SUFFIX__) # define UINT24_C(v) __uint_c(v, __INT24_C_SUFFIX__) # define __int16_c_suffix __INT24_C_SUFFIX__ # define __int8_c_suffix __INT24_C_SUFFIX__ # else # define INT24_C(v) v # define UINT24_C(v) v ## U # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT24_C_SUFFIX__ */ #endif /* __INT24_TYPE__ */ #ifdef __INT16_TYPE__ # ifdef __INT16_C_SUFFIX__ # define __int16_c_suffix __INT16_C_SUFFIX__ # define __int8_c_suffix __INT16_C_SUFFIX__ #else # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT16_C_SUFFIX__ */ #endif /* __INT16_TYPE__ */ #ifdef __int_least16_t # ifdef __int16_c_suffix # define INT16_C(v) __int_c(v, __int16_c_suffix) # define UINT16_C(v) __uint_c(v, __int16_c_suffix) # else # define INT16_C(v) v # define UINT16_C(v) v ## U # endif /* __int16_c_suffix */ #endif /* __int_least16_t */ #ifdef __INT8_TYPE__ # ifdef __INT8_C_SUFFIX__ # define __int8_c_suffix __INT8_C_SUFFIX__ #else # undef __int8_c_suffix # endif /* __INT8_C_SUFFIX__ */ #endif /* __INT8_TYPE__ */ #ifdef __int_least8_t # ifdef __int8_c_suffix # define INT8_C(v) __int_c(v, __int8_c_suffix) # define UINT8_C(v) __uint_c(v, __int8_c_suffix) # else # define INT8_C(v) v # define UINT8_C(v) v ## U # endif /* __int8_c_suffix */ #endif /* __int_least8_t */ /* C99 7.18.2.1 Limits of exact-width integer types. * C99 7.18.2.2 Limits of minimum-width integer types. * C99 7.18.2.3 Limits of fastest minimum-width integer types. * * The presence of limit macros are completely optional in C99. This * implementation defines limits for all of the types (exact- and * minimum-width) that it defines above, using the limits of the minimum-width * type for any types that do not have exact-width representations. * * As in the type definitions, this section takes an approach of * successive-shrinking to determine which limits to use for the standard (8, * 16, 32, 64) bit widths when they don't have exact representations. It is * therefore important that the definitions be kept in order of decending * widths. * * Note that C++ should not check __STDC_LIMIT_MACROS here, contrary to the * claims of the C standard (see C++ 18.3.1p2, [cstdint.syn]). */ #ifdef __INT64_TYPE__ # define INT64_MAX INT64_C( 9223372036854775807) # define INT64_MIN (-INT64_C( 9223372036854775807)-1) # define UINT64_MAX UINT64_C(18446744073709551615) # define __INT_LEAST64_MIN INT64_MIN # define __INT_LEAST64_MAX INT64_MAX # define __UINT_LEAST64_MAX UINT64_MAX # define __INT_LEAST32_MIN INT64_MIN # define __INT_LEAST32_MAX INT64_MAX # define __UINT_LEAST32_MAX UINT64_MAX # define __INT_LEAST16_MIN INT64_MIN # define __INT_LEAST16_MAX INT64_MAX # define __UINT_LEAST16_MAX UINT64_MAX # define __INT_LEAST8_MIN INT64_MIN # define __INT_LEAST8_MAX INT64_MAX # define __UINT_LEAST8_MAX UINT64_MAX #endif /* __INT64_TYPE__ */ #ifdef __INT_LEAST64_MIN # define INT_LEAST64_MIN __INT_LEAST64_MIN # define INT_LEAST64_MAX __INT_LEAST64_MAX # define UINT_LEAST64_MAX __UINT_LEAST64_MAX # define INT_FAST64_MIN __INT_LEAST64_MIN # define INT_FAST64_MAX __INT_LEAST64_MAX # define UINT_FAST64_MAX __UINT_LEAST64_MAX #endif /* __INT_LEAST64_MIN */ #ifdef __INT56_TYPE__ # define INT56_MAX INT56_C(36028797018963967) # define INT56_MIN (-INT56_C(36028797018963967)-1) # define UINT56_MAX UINT56_C(72057594037927935) # define INT_LEAST56_MIN INT56_MIN # define INT_LEAST56_MAX INT56_MAX # define UINT_LEAST56_MAX UINT56_MAX # define INT_FAST56_MIN INT56_MIN # define INT_FAST56_MAX INT56_MAX # define UINT_FAST56_MAX UINT56_MAX # define __INT_LEAST32_MIN INT56_MIN # define __INT_LEAST32_MAX INT56_MAX # define __UINT_LEAST32_MAX UINT56_MAX # define __INT_LEAST16_MIN INT56_MIN # define __INT_LEAST16_MAX INT56_MAX # define __UINT_LEAST16_MAX UINT56_MAX # define __INT_LEAST8_MIN INT56_MIN # define __INT_LEAST8_MAX INT56_MAX # define __UINT_LEAST8_MAX UINT56_MAX #endif /* __INT56_TYPE__ */ #ifdef __INT48_TYPE__ # define INT48_MAX INT48_C(140737488355327) # define INT48_MIN (-INT48_C(140737488355327)-1) # define UINT48_MAX UINT48_C(281474976710655) # define INT_LEAST48_MIN INT48_MIN # define INT_LEAST48_MAX INT48_MAX # define UINT_LEAST48_MAX UINT48_MAX # define INT_FAST48_MIN INT48_MIN # define INT_FAST48_MAX INT48_MAX # define UINT_FAST48_MAX UINT48_MAX # define __INT_LEAST32_MIN INT48_MIN # define __INT_LEAST32_MAX INT48_MAX # define __UINT_LEAST32_MAX UINT48_MAX # define __INT_LEAST16_MIN INT48_MIN # define __INT_LEAST16_MAX INT48_MAX # define __UINT_LEAST16_MAX UINT48_MAX # define __INT_LEAST8_MIN INT48_MIN # define __INT_LEAST8_MAX INT48_MAX # define __UINT_LEAST8_MAX UINT48_MAX #endif /* __INT48_TYPE__ */ #ifdef __INT40_TYPE__ # define INT40_MAX INT40_C(549755813887) # define INT40_MIN (-INT40_C(549755813887)-1) # define UINT40_MAX UINT40_C(1099511627775) # define INT_LEAST40_MIN INT40_MIN # define INT_LEAST40_MAX INT40_MAX # define UINT_LEAST40_MAX UINT40_MAX # define INT_FAST40_MIN INT40_MIN # define INT_FAST40_MAX INT40_MAX # define UINT_FAST40_MAX UINT40_MAX # define __INT_LEAST32_MIN INT40_MIN # define __INT_LEAST32_MAX INT40_MAX # define __UINT_LEAST32_MAX UINT40_MAX # define __INT_LEAST16_MIN INT40_MIN # define __INT_LEAST16_MAX INT40_MAX # define __UINT_LEAST16_MAX UINT40_MAX # define __INT_LEAST8_MIN INT40_MIN # define __INT_LEAST8_MAX INT40_MAX # define __UINT_LEAST8_MAX UINT40_MAX #endif /* __INT40_TYPE__ */ #ifdef __INT32_TYPE__ # define INT32_MAX INT32_C(2147483647) # define INT32_MIN (-INT32_C(2147483647)-1) # define UINT32_MAX UINT32_C(4294967295) # define __INT_LEAST32_MIN INT32_MIN # define __INT_LEAST32_MAX INT32_MAX # define __UINT_LEAST32_MAX UINT32_MAX # define __INT_LEAST16_MIN INT32_MIN # define __INT_LEAST16_MAX INT32_MAX # define __UINT_LEAST16_MAX UINT32_MAX # define __INT_LEAST8_MIN INT32_MIN # define __INT_LEAST8_MAX INT32_MAX # define __UINT_LEAST8_MAX UINT32_MAX #endif /* __INT32_TYPE__ */ #ifdef __INT_LEAST32_MIN # define INT_LEAST32_MIN __INT_LEAST32_MIN # define INT_LEAST32_MAX __INT_LEAST32_MAX # define UINT_LEAST32_MAX __UINT_LEAST32_MAX # define INT_FAST32_MIN __INT_LEAST32_MIN # define INT_FAST32_MAX __INT_LEAST32_MAX # define UINT_FAST32_MAX __UINT_LEAST32_MAX #endif /* __INT_LEAST32_MIN */ #ifdef __INT24_TYPE__ # define INT24_MAX INT24_C(8388607) # define INT24_MIN (-INT24_C(8388607)-1) # define UINT24_MAX UINT24_C(16777215) # define INT_LEAST24_MIN INT24_MIN # define INT_LEAST24_MAX INT24_MAX # define UINT_LEAST24_MAX UINT24_MAX # define INT_FAST24_MIN INT24_MIN # define INT_FAST24_MAX INT24_MAX # define UINT_FAST24_MAX UINT24_MAX # define __INT_LEAST16_MIN INT24_MIN # define __INT_LEAST16_MAX INT24_MAX # define __UINT_LEAST16_MAX UINT24_MAX # define __INT_LEAST8_MIN INT24_MIN # define __INT_LEAST8_MAX INT24_MAX # define __UINT_LEAST8_MAX UINT24_MAX #endif /* __INT24_TYPE__ */ #ifdef __INT16_TYPE__ #define INT16_MAX INT16_C(32767) #define INT16_MIN (-INT16_C(32767)-1) #define UINT16_MAX UINT16_C(65535) # define __INT_LEAST16_MIN INT16_MIN # define __INT_LEAST16_MAX INT16_MAX # define __UINT_LEAST16_MAX UINT16_MAX # define __INT_LEAST8_MIN INT16_MIN # define __INT_LEAST8_MAX INT16_MAX # define __UINT_LEAST8_MAX UINT16_MAX #endif /* __INT16_TYPE__ */ #ifdef __INT_LEAST16_MIN # define INT_LEAST16_MIN __INT_LEAST16_MIN # define INT_LEAST16_MAX __INT_LEAST16_MAX # define UINT_LEAST16_MAX __UINT_LEAST16_MAX # define INT_FAST16_MIN __INT_LEAST16_MIN # define INT_FAST16_MAX __INT_LEAST16_MAX # define UINT_FAST16_MAX __UINT_LEAST16_MAX #endif /* __INT_LEAST16_MIN */ #ifdef __INT8_TYPE__ # define INT8_MAX INT8_C(127) # define INT8_MIN (-INT8_C(127)-1) # define UINT8_MAX UINT8_C(255) # define __INT_LEAST8_MIN INT8_MIN # define __INT_LEAST8_MAX INT8_MAX # define __UINT_LEAST8_MAX UINT8_MAX #endif /* __INT8_TYPE__ */ #ifdef __INT_LEAST8_MIN # define INT_LEAST8_MIN __INT_LEAST8_MIN # define INT_LEAST8_MAX __INT_LEAST8_MAX # define UINT_LEAST8_MAX __UINT_LEAST8_MAX # define INT_FAST8_MIN __INT_LEAST8_MIN # define INT_FAST8_MAX __INT_LEAST8_MAX # define UINT_FAST8_MAX __UINT_LEAST8_MAX #endif /* __INT_LEAST8_MIN */ /* Some utility macros */ #define __INTN_MIN(n) __stdint_join3( INT, n, _MIN) #define __INTN_MAX(n) __stdint_join3( INT, n, _MAX) #define __UINTN_MAX(n) __stdint_join3(UINT, n, _MAX) #define __INTN_C(n, v) __stdint_join3( INT, n, _C(v)) #define __UINTN_C(n, v) __stdint_join3(UINT, n, _C(v)) /* C99 7.18.2.4 Limits of integer types capable of holding object pointers. */ /* C99 7.18.3 Limits of other integer types. */ #define INTPTR_MIN (-__INTPTR_MAX__-1) #define INTPTR_MAX __INTPTR_MAX__ #define UINTPTR_MAX __UINTPTR_MAX__ #define PTRDIFF_MIN (-__PTRDIFF_MAX__-1) #define PTRDIFF_MAX __PTRDIFF_MAX__ #define SIZE_MAX __SIZE_MAX__ /* ISO9899:2011 7.20 (C11 Annex K): Define RSIZE_MAX if __STDC_WANT_LIB_EXT1__ * is enabled. */ #if defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1 #define RSIZE_MAX (SIZE_MAX >> 1) #endif /* C99 7.18.2.5 Limits of greatest-width integer types. */ #define INTMAX_MIN (-__INTMAX_MAX__-1) #define INTMAX_MAX __INTMAX_MAX__ #define UINTMAX_MAX __UINTMAX_MAX__ /* C99 7.18.3 Limits of other integer types. */ #define SIG_ATOMIC_MIN __INTN_MIN(__SIG_ATOMIC_WIDTH__) #define SIG_ATOMIC_MAX __INTN_MAX(__SIG_ATOMIC_WIDTH__) #ifdef __WINT_UNSIGNED__ # define WINT_MIN __UINTN_C(__WINT_WIDTH__, 0) # define WINT_MAX __UINTN_MAX(__WINT_WIDTH__) #else # define WINT_MIN __INTN_MIN(__WINT_WIDTH__) # define WINT_MAX __INTN_MAX(__WINT_WIDTH__) #endif #ifndef WCHAR_MAX # define WCHAR_MAX __WCHAR_MAX__ #endif #ifndef WCHAR_MIN # if __WCHAR_MAX__ == __INTN_MAX(__WCHAR_WIDTH__) # define WCHAR_MIN __INTN_MIN(__WCHAR_WIDTH__) # else # define WCHAR_MIN __UINTN_C(__WCHAR_WIDTH__, 0) # endif #endif /* 7.18.4.2 Macros for greatest-width integer constants. */ #define INTMAX_C(v) __int_c(v, __INTMAX_C_SUFFIX__) #define UINTMAX_C(v) __int_c(v, __UINTMAX_C_SUFFIX__) #endif /* __STDC_HOSTED__ */ #endif /* __CLANG_STDINT_H */ bpftrace-0.14.0/scripts/000077500000000000000000000000001413460502400150505ustar00rootroot00000000000000bpftrace-0.14.0/scripts/check_kernel_features.sh000077500000000000000000000022441413460502400217240ustar00rootroot00000000000000#!/bin/sh # Report missing kernel features # # Usage: ./check_kernel_features.sh set -e set -u err=0 config='' # Find kernel config for c in "/boot/config-$(uname -r)" "/boot/config" "/proc/config.gz"; do if [ -f "$c" ]; then config="$c" break fi done if [ -z "$config" ]; then echo "Could not find kernel config" >&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.14.0/scripts/compare_tool_codegen.sh000077500000000000000000000033351413460502400215620ustar00rootroot00000000000000#!/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 /vagrant/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.14.0/scripts/compare_tool_elf.sh000077500000000000000000000031641413460502400207240ustar00rootroot00000000000000#!/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 /vagrant/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.14.0/scripts/compare_tool_speed.sh000077500000000000000000000041671413460502400212620ustar00rootroot00000000000000#!/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 /vagrant/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" && $TESTMODE != "semantic" ]]; 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.14.0/scripts/create-assets.sh000077500000000000000000000032511413460502400201530ustar00rootroot00000000000000#!/bin/bash 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" [[ -f bpftrace ]] || err "bpftrace binary not found, download the build artifact" 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" 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/* "$TMP/bin" rm "$TMP/bin/CMakeLists.txt" chmod +x "$TMP/bin/"*.bt tar --xz -cf "$OUT/tools-with-help.tar.xz" -C "$TMP/bin" "." rm "$TMP/bin/"*.txt tar --xz -cf "$OUT/tools.tar.xz" -C "$TMP/bin" "." info "Creating man archive" mkdir -p "$TMP/share/man/man8" cp man/man8/*.8 "$TMP/share//man/man8/" gzip "$TMP/share/man/man8/"* asciidoctor man/adoc/bpftrace.adoc -b manpage -o - | gzip - > "$TMP/share/man/man8/bpftrace.8.gz" tar --xz -cf "$OUT/man.tar.xz" -C "$TMP/share" man info "Creating bundle" ln bpftrace "$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" info "Compressing binary" cp bpftrace "$OUT/" xz -k "$OUT/bpftrace" zstd $ZSTDFLAGS -q -k "$OUT/bpftrace" echo "All assets created in $OUT" [[ -d "$TMP" ]] && rm -rf "$TMP" bpftrace-0.14.0/scripts/seccomp.c000066400000000000000000000125011413460502400166440ustar00rootroot00000000000000// 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.14.0/scripts/tracepoint_variable_sized_types.py000066400000000000000000000021771413460502400240700ustar00rootroot00000000000000# 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 in glob.iglob("/sys/kernel/debug/tracing/events/*/*/format"): for line in open(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.14.0/scripts/update_codegen_tests.sh000077500000000000000000000014061413460502400216000ustar00rootroot00000000000000#!/bin/bash # Updates codegen tests' expected LLVM IR # set -eu # Change dir to project root cd "$(dirname "${BASH_SOURCE[0]}")" cd .. # Build docker image pushd docker docker build \ --network host \ --build-arg LLVM_VERSION=12 \ -t bpftrace-builder-bionic \ -f Dockerfile.bionic \ . popd # Update IR docker run \ --network host \ --rm \ -it \ -v $(pwd):$(pwd) \ -e BPFTRACE_UPDATE_TESTS=1 \ -e TEST_ARGS="--gtest_filter=codegen.*" \ -e VENDOR_GTEST="ON" \ bpftrace-builder-bionic "$(pwd)/build-codegen-update" Debug "$@" bpftrace-0.14.0/snap/000077500000000000000000000000001413460502400143225ustar00rootroot00000000000000bpftrace-0.14.0/snap/snapcraft.yaml000066400000000000000000000102171413460502400171700ustar00rootroot00000000000000# # To build: # # Using multipass, one should enable a large multipass VM using: # # SNAPCRAFT_BUILD_ENVIRONMENT_MEMORY=8G snapcraft # # Alternatively, one can build in a lxd container using: # # snapcraft --use-lxd # # Install using something like: # # sudo snap install --devmode bpftrace_0.9.4-20200312-1403-11814f2_amd64.snap # name: bpftrace summary: high-level tracing language for Linux eBPF description: BPFtrace is a high-level tracing language for Linux enhanced Berkeley Packet Filter available in recent Linux kernels. BPFtrace uses LLVM as a backend to compile scripts to BPF-bytecode and makes use of BCC for interacting with the Linux BPF system, as well as existing Linux tracing capabilities like kernel dynamic tracing, user-level dynamic tracing and tracepoints. confinement: strict grade: stable base: core20 assumes: [snapd2.37] adopt-info: bpftrace architectures: - build-on: amd64 - build-on: arm64 parts: bcclibs3: plugin: cmake source: https://github.com/iovisor/bcc.git source-tag: v0.8.0 cmake-parameters: - -DLLVM_DEFINITIONS="-D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" - -DREVISION_LAST=v0.21.0 - -DBCC_KERNEL_HAS_SOURCE_DIR=1 - -DPYTHON_CMD='python3' - -DCMAKE_INSTALL_PREFIX=/usr override-pull: | snapcraftctl pull sed -i "s;if os.environ.get('DESTDIR'):;if \"sdist\" not in sys.argv and os.environ.get('DESTDIR'):;" src/python/setup.py.in build-packages: - git - libc6 - iperf - bison - cmake - flex - g++ - binutils-dev - libstdc++-dev - libelf-dev - zlib1g-dev - libfl-dev - clang-7 - libclang-7-dev - libclang-common-7-dev - libclang1-7 - libllvm7 - llvm-7 - llvm-7-dev - llvm-7-runtime - systemtap-sdt-dev - netperf - python3 - arping - clang-format - ethtool - inetutils-ping - libedit-dev - libzip-dev - libluajit-5.1-dev - luajit - python3-netaddr - python3-pyroute2 - python3-distutils stage: - usr/lib/$SNAPCRAFT_ARCH_TRIPLET/* - usr/lib/* - usr/include/* - usr/include/bcc/* stage-packages: - libbinutils - libclang1-7 - libllvm7 bpftrace: after: - bcclibs3 source: https://github.com/iovisor/bpftrace.git source-tag: v0.13.0 plugin: cmake cmake-parameters: - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_LIBRARY_PATH=$SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ - -DLIBBCC_INCLUDE_DIRS=$SNAPCRAFT_STAGE/usr/include/ - -DCMAKE_INSTALL_PREFIX=/usr override-pull: | snapcraftctl pull description=$(git describe HEAD --tags) sha=$(echo $description | tr '-' ' ' | awk '{print $NF}') version=${description%$sha} commits=$(git log --oneline | wc -l) date=$(date +'%Y%m%d') version=$(echo $version$date-$commits-$sha | cut -c1-32) snapcraftctl set-version "$version" build-packages: - bison - cmake - flex - g++ - git - libelf-dev - zlib1g-dev - libfl-dev - systemtap-sdt-dev - binutils-dev - llvm-7-dev - llvm-7-runtime - libclang-7-dev - libcereal-dev - clang-7 - libgtest-dev - libgmock-dev - asciidoctor stage-packages: - libbinutils - libclang1-7 - libllvm7 stage: - usr/bin - usr/share apps: bpftrace: command: usr/bin/bpftrace plugs: [ home, system-trace ] bpftrace-0.14.0/src/000077500000000000000000000000001413460502400141505ustar00rootroot00000000000000bpftrace-0.14.0/src/CMakeLists.txt000066400000000000000000000144131413460502400167130ustar00rootroot00000000000000if(HAVE_BFD_DISASM) set(BFD_DISASM_SRC bfd-disasm.cpp) endif() if(BUILD_FUZZ) message(STATUS "Build for fuzzing") set(MAIN_SRC fuzz_main.cpp) set(BPFTRACE bpftrace_fuzz) else() set(MAIN_SRC main.cpp) if (NOT DEFINED BPFTRACE) set(BPFTRACE bpftrace) endif () endif() add_library(runtime attached_probe.cpp bpffeature.cpp bpftrace.cpp btf.cpp child.cpp disasm.cpp dwarf_parser.cpp fake_map.cpp imap.cpp log.cpp map.cpp mapkey.cpp output.cpp probe_matcher.cpp procmon.cpp printf.cpp relocator.cpp resolve_cgroupid.cpp required_resources.cpp struct.cpp types.cpp usdt.cpp utils.cpp ${BFD_DISASM_SRC} ) # Ensure flex+bison outputs are built first add_dependencies(runtime parser) add_library(libbpftrace build_info.cpp clang_parser.cpp driver.cpp lockdown.cpp tracepoint_format_parser.cpp ) # So it's not "liblibbpftrace" set_target_properties(libbpftrace PROPERTIES PREFIX "") add_executable(${BPFTRACE} ${MAIN_SRC} ) install(TARGETS ${BPFTRACE} DESTINATION ${CMAKE_INSTALL_BINDIR}) target_link_libraries(${BPFTRACE} libbpftrace) if (BUILD_FUZZ) target_compile_options(${BPFTRACE} PUBLIC "-DFUZZ") if(FUZZ_TARGET STREQUAL "semantic") message(STATUS "Fuzzing seantic analyzer") target_compile_options(${BPFTRACE} PUBLIC -DTEST_SEMANTIC) elseif(FUZZ_TARGET STREQUAL "codegen") message(STATUS "Fuzzing codegen") else() message(FATAL_ERROR "Unsupported FUZZ_TARGET") endif() if (USE_LIBFUZZER) if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") message(FATAL_ERROR "Use Clang for libfuzzer") endif() target_compile_options(${BPFTRACE} PUBLIC "-DLIBFUZZER") message(STATUS "Use libfuzzer") endif() endif() # compile definitions set(KERNEL_HEADERS_DIR "" CACHE PATH "Hard-code kernel headers directory") if (KERNEL_HEADERS_DIR) MESSAGE(STATUS "Using KERNEL_HEADERS_DIR=${KERNEL_HEADERS_DIR}") target_compile_definitions(runtime PUBLIC KERNEL_HEADERS_DIR="${KERNEL_HEADERS_DIR}") endif() execute_process( COMMAND git describe --abbrev=4 --dirty --tags WORKING_DIRECTORY ${CMAKE_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}" STREQUAL "0") set(BPFTRACE_VERSION "v${bpftrace_VERSION_MAJOR}.${bpftrace_VERSION_MINOR}.${bpftrace_VERSION_PATCH}") endif() add_definitions("-DBPFTRACE_VERSION=\"${BPFTRACE_VERSION}\"") target_include_directories(runtime PRIVATE ${CMAKE_BINARY_DIR}) target_include_directories(runtime PRIVATE ${CMAKE_SOURCE_DIR}/src) target_include_directories(runtime PRIVATE ${CMAKE_SOURCE_DIR}/src/ast) target_compile_definitions(runtime PRIVATE ${BPFTRACE_FLAGS}) target_compile_definitions(libbpftrace PRIVATE ${BPFTRACE_FLAGS}) # Linking if(STATIC_LINKING) if(STATIC_LIBC) target_link_options(${BPFTRACE} BEFORE PRIVATE "-static") else() target_link_options(${BPFTRACE} BEFORE PRIVATE "-static-libgcc" "-static-libstdc++") endif(STATIC_LIBC) endif(STATIC_LINKING) target_link_libraries(libbpftrace parser resources runtime aot ast arch cxxdemangler_llvm) if (LIBBPF_BTF_DUMP_FOUND) target_include_directories(runtime PUBLIC ${LIBBPF_INCLUDE_DIRS}) target_link_libraries(runtime ${LIBBPF_LIBRARIES}) endif(LIBBPF_BTF_DUMP_FOUND) if(HAVE_BFD_DISASM) if(STATIC_LINKING) 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) else() target_link_libraries(runtime ${LIBBFD_LIBRARIES}) target_link_libraries(runtime ${LIBOPCODES_LIBRARIES}) endif(STATIC_LINKING) 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}) 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) add_library(LIBDW STATIC IMPORTED) set_property(TARGET LIBDW PROPERTY IMPORTED_LOCATION ${LIBDW_LIBRARIES}) 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 (USE_LIBFUZZER) if (BUILD_ASAN) target_compile_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer,address") target_link_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer") else() target_compile_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer") target_link_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer,address") endif() endif() if (STATIC_LINKING) 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() unset(MAIN_SRC) unset(BPFTRACE) add_subdirectory(aot) add_subdirectory(arch) add_subdirectory(ast) add_subdirectory(cxxdemangler) bpftrace-0.14.0/src/act_helpers.h000066400000000000000000000035631413460502400166210ustar00rootroot00000000000000#pragma once // Annoying C type helpers. // // C headers sometimes contain struct ending with a flexible array // member, which is not supported in C++. An example of such a type is // file_handle from fcntl.h (man name_to_handle_at) // // Here are some helper macros helping to ensure if the C++ // counterpart has members of the same type and offset. #include #include namespace act_helpers { template M get_member_type(M T:: *); } #define ACTH_SAME_SIZE(cxxtype, ctype, extra_type) \ (sizeof(cxxtype) == sizeof(ctype) + sizeof(extra_type)) #define ACTH_GET_TYPE_OF(mem) \ decltype(act_helpers::get_member_type(&mem)) #define ACTH_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem) \ (std::is_standard_layout::value && \ std::is_standard_layout::value && \ (offsetof(cxxtype, cxxmem) == offsetof(ctype, cmem))) #define ACTH_SAME_TYPE(cxxtype, cxxmem, ctype, cmem) \ std::is_same::value #define ACTH_ASSERT_SAME_SIZE(cxxtype, ctype, extra_type) \ static_assert(ACTH_SAME_SIZE(cxxtype, ctype, extra_type), \ "assumption that is broken: " #cxxtype " == " #ctype " + " # extra_type) #define ACTH_ASSERT_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem) \ static_assert(ACTH_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem), \ "assumption that is broken: " #cxxtype "::" #cxxmem " is at the same offset as " #ctype "::" #cmem) #define ACTH_ASSERT_SAME_TYPE(cxxtype, cxxmem, ctype, cmem) \ static_assert(ACTH_SAME_TYPE(cxxtype, cxxmem, ctype, cmem), \ "assumption that is broken: " #cxxtype "::" #cxxmem " has the same type as " #ctype "::" #cmem) #define ACTH_ASSERT_SAME_MEMBER(cxxtype, cxxmem, ctype, cmem) \ ACTH_ASSERT_SAME_TYPE(cxxtype, cxxmem, ctype, cmem); \ ACTH_ASSERT_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem) bpftrace-0.14.0/src/aot/000077500000000000000000000000001413460502400147335ustar00rootroot00000000000000bpftrace-0.14.0/src/aot/CMakeLists.txt000066400000000000000000000017611413460502400175000ustar00rootroot00000000000000add_library(aot aot.cpp) target_include_directories(aot PUBLIC ${CMAKE_SOURCE_DIR}/src) target_include_directories(aot PUBLIC ${CMAKE_BINARY_DIR}) target_compile_definitions(aot PRIVATE ${BPFTRACE_FLAGS}) # 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_link_libraries(bpftrace-aotrt aot runtime arch ast_defs cxxdemangler_stdlib) install(TARGETS bpftrace-aotrt DESTINATION ${CMAKE_INSTALL_BINDIR}) # Linking if(STATIC_LINKING) if(STATIC_LIBC) target_link_options(bpftrace-aotrt BEFORE PRIVATE "-static") else() target_link_options(bpftrace-aotrt BEFORE PRIVATE "-static-libgcc" "-static-libstdc++") endif(STATIC_LIBC) 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.14.0/src/aot/aot.cpp000066400000000000000000000143131413460502400162240ustar00rootroot00000000000000#include "aot.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "utils.h" static constexpr auto AOT_MAGIC = 0xA07; // 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 bc_off; // Bytecode offset from start of file uint64_t bc_len; // Bytecode length }; static_assert(sizeof(Header) == 48); static_assert(sizeof(std::size_t) <= sizeof(uint64_t)); namespace bpftrace { namespace aot { namespace { uint32_t rs_hash(const std::string &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; } void serialize_bytecode(const BpfBytecode &bytecode, std::ostream &out) { cereal::BinaryOutputArchive archive(out); archive(bytecode); } 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; } int load_bytecode(BPFtrace &bpftrace, uint8_t *ptr, size_t len) { try { Membuf mbuf(ptr, ptr + len); std::istream istream(&mbuf); cereal::BinaryInputArchive archive(istream); archive(bpftrace.bytecode_); } catch (const std::exception &ex) { LOG(ERROR) << "Failed to deserialize metadata: " << ex.what(); return 1; } return 0; } } // namespace int generate(const RequiredResources &resources, const BpfBytecode &bytecode, const std::string &out) { // 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 1; } // Serialize bytecode std::string serialized_bytecode; try { std::ostringstream serialized(std::ios::binary); serialize_bytecode(bytecode, serialized); serialized_bytecode = serialized.str(); } catch (const std::exception &ex) { LOG(ERROR) << "Failed to serialize bytecode: " << ex.what(); return 1; } 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(), .bc_off = hdr_len + serialized_metadata.size(), .bc_len = serialized_bytecode.size(), }; std::ofstream outf(out, std::ios::binary | std::ios::out | std::ios::trunc); if (outf.fail()) { LOG(ERROR) << "Failed to open: " << out; return 1; } // Write out header outf.write(reinterpret_cast(&hdr), sizeof(hdr)); if (outf.fail()) { LOG(ERROR) << "Failed to write header to: " << out; return 1; } // Write out metadata outf << serialized_metadata; if (outf.fail()) { LOG(ERROR) << "Failed to write metadata to:" << out; return 1; } // Write out bytecode outf << serialized_bytecode; if (outf.fail()) { LOG(ERROR) << "Failed to write bytecode to:" << out; return 1; } outf.flush(); if (outf.fail()) { LOG(ERROR) << "Failed to flush: " << out; return 1; } 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; } struct stat sb; if (fstat(infd, &sb)) { auto saved_err = errno; LOG(ERROR) << "Failed to stat: " << in << ": " << std::strerror(saved_err); return 1; } uint8_t *ptr = static_cast( ::mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, infd, 0)); if (ptr == MAP_FAILED) { auto saved_err = errno; LOG(ERROR) << "Failed to mmap: " << in << ": " << std::strerror(saved_err); return 1; } // Validate header auto hdr = reinterpret_cast(ptr); 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(sb.st_size) || (hdr->bc_off + hdr->bc_len) > static_cast(sb.st_size)) { LOG(ERROR) << "Corrupted AOT bpftrace file: incomplete payload"; err = 1; goto out; } // Load payloads err = load_required_resources(bpftrace, ptr + hdr->rr_off, hdr->rr_len); if (err) goto out; err = load_bytecode(bpftrace, ptr + hdr->bc_off, hdr->bc_len); if (err) goto out; out: if (::munmap(ptr, sb.st_size)) { auto saved_err = errno; LOG(ERROR) << "Failed to munmap(): " << in << ": " << std::strerror(saved_err); } return err; } } // namespace aot } // namespace bpftrace bpftrace-0.14.0/src/aot/aot.h000066400000000000000000000005631413460502400156730ustar00rootroot00000000000000#pragma once #include #include #include "bpftrace.h" #include "required_resources.h" namespace bpftrace { namespace aot { int generate(const RequiredResources &resources, const BpfBytecode &bytecode, const std::string &out); int load(BPFtrace &bpftrace, const std::string &in); } // namespace aot } // namespace bpftrace bpftrace-0.14.0/src/aot/aot_main.cpp000066400000000000000000000061241413460502400172310ustar00rootroot00000000000000#include #include #include #include #include "aot.h" #include "bpftrace.h" #include "log.h" #include "output.h" using namespace bpftrace; void usage() { // clang-format off std::cerr << "USAGE: bpftrace-aotrt filename" << std::endl; std::cerr << std::endl; std::cerr << "OPTIONS:" << std::endl; std::cerr << " -f FORMAT output format ('text', 'json')" << std::endl; std::cerr << " -o file redirect bpftrace output to file" << std::endl; std::cerr << " -q, keep messages quiet" << std::endl; std::cerr << " -v, verbose messages" << std::endl; std::cerr << " -h, --help show this help message" << std::endl; std::cerr << " -V, --version bpftrace version" << std::endl; std::cerr << std::endl; // clang-format on return; } std::unique_ptr prepare_output(const std::string& output_file, const std::string& output_format) { std::ostream* os = &std::cout; std::ofstream outputstream; if (!output_file.empty()) { outputstream.open(output_file); if (outputstream.fail()) { LOG(ERROR) << "Failed to open output file: \"" << output_file << "\": " << strerror(errno); return nullptr; } os = &outputstream; } std::unique_ptr output; if (output_format.empty() || output_format == "text") { output = std::make_unique(*os); } else if (output_format == "json") { output = std::make_unique(*os); } else { LOG(ERROR) << "Invalid output format \"" << output_format << "\"\n" << "Valid formats: 'text', 'json'"; return nullptr; } return output; } int main(int argc, char* argv[]) { std::string output_file, output_format; int c; // TODO: which other options from `bpftrace` should be included? const char* const short_opts = "f:hVo:qv"; option long_opts[] = { option{ "help", no_argument, nullptr, 'h' }, option{ "version", no_argument, nullptr, 'V' }, option{ nullptr, 0, nullptr, 0 }, // Must be last }; 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(); 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; default: usage(); return 1; } } if (!argv[optind]) { usage(); return 1; } auto output = prepare_output(output_file, output_format); if (!output) return 1; BPFtrace bpftrace(std::move(output)); int err = aot::load(bpftrace, argv[optind]); if (err) { LOG(ERROR) << "Failed to load AOT script"; return err; } err = bpftrace.run(std::move(bpftrace.bytecode_)); if (err) return err; std::cout << "\n\n"; return bpftrace.print_maps(); return 0; } bpftrace-0.14.0/src/arch/000077500000000000000000000000001413460502400150655ustar00rootroot00000000000000bpftrace-0.14.0/src/arch/CMakeLists.txt000066400000000000000000000011241413460502400176230ustar00rootroot00000000000000if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") add_library(arch aarch64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le") add_library(arch ppc64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "s390" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "s390x") add_library(arch s390.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") add_library(arch x86_64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips64") add_library(arch mips64.cpp) else() message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() bpftrace-0.14.0/src/arch/aarch64.cpp000066400000000000000000000044731413460502400170310ustar00rootroot00000000000000#include "arch.h" #include #include // SP points to the first argument that is passed on the stack #define ARG0_STACK 0 namespace bpftrace { namespace arch { // clang-format off static std::array registers = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "sp", "pc", "pstate", }; // Alternative register names that match struct pt_regs static std::array ptrace_registers = { "regs[0]", "regs[1]", "regs[2]", "regs[3]", "regs[4]", "regs[5]", "regs[6]", "regs[7]", "regs[8]", "regs[9]", "regs[10]", "regs[11]", "regs[12]", "regs[13]", "regs[14]", "regs[15]", "regs[16]", "regs[17]", "regs[18]", "regs[19]", "regs[20]", "regs[21]", "regs[22]", "regs[23]", "regs[24]", "regs[25]", "regs[26]", "regs[27]", "regs[28]", "regs[29]", "regs[30]", "sp", "pc", "pstate", }; static std::array arg_registers = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", }; // clang-format on int offset(std::string reg_name) { auto it = find(registers.begin(), registers.end(), reg_name); if (it == registers.end()) { // Also allow register names that match the fields in struct pt_regs. // These appear in USDT probe arguments. it = find(ptrace_registers.begin(), ptrace_registers.end(), reg_name); if (it == ptrace_registers.end()) return -1; return distance(ptrace_registers.begin(), it); } return distance(registers.begin(), it); } int max_arg() { return arg_registers.size() - 1; } int arg_offset(int arg_num) { return offset(arg_registers.at(arg_num)); } int ret_offset() { return offset("r0"); } int pc_offset() { return offset("pc"); } int sp_offset() { return offset("sp"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("aarch64"); } std::vector invalid_watchpoint_modes() { // See arch/arm/kernel/hw_breakpoint.c:arch_build_bp_info in kernel source return std::vector{ "rx", "wx", "rwx", }; } } // namespace arch } // namespace bpftrace bpftrace-0.14.0/src/arch/arch.h000066400000000000000000000006671413460502400161640ustar00rootroot00000000000000#pragma once #include #include #include namespace bpftrace { namespace arch { int offset(std::string reg_name); int max_arg(); int arg_offset(int arg_num); int ret_offset(); int pc_offset(); int sp_offset(); int arg_stack_offset(); std::string name(); // Each string is lexicographically sorted by character std::vector invalid_watchpoint_modes(); } // namespace arch } // namespace bpftrace bpftrace-0.14.0/src/arch/mips64.cpp000066400000000000000000000047211413460502400167170ustar00rootroot00000000000000#include "arch.h" #include #include // SP + 8 points to the first argument that is passed on the stack #define ARG0_STACK 8 namespace bpftrace { namespace arch { // clang-format off static std::array registers = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp/s8", "ra", }; // Alternative register names that match struct pt_regs static std::array ptrace_registers = { "regs[0]", "regs[1]", "regs[2]", "regs[3]", "regs[4]", "regs[5]", "regs[6]", "regs[7]", "regs[8]", "regs[9]", "regs[10]", "regs[11]", "regs[12]", "regs[13]", "regs[14]", "regs[15]", "regs[16]", "regs[17]", "regs[18]", "regs[19]", "regs[20]", "regs[21]", "regs[22]", "regs[23]", "regs[24]", "regs[25]", "regs[26]", "regs[27]", "regs[28]", "regs[29]", "regs[30]", "regs[31]", }; static std::array arg_registers = { "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", }; // clang-format on int offset(std::string reg_name) { auto it = find(registers.begin(), registers.end(), reg_name); if (it == registers.end()) { // Also allow register names that match the fields in struct pt_regs. // These appear in USDT probe arguments. it = find(ptrace_registers.begin(), ptrace_registers.end(), reg_name); if (it == ptrace_registers.end()) { return -1; } return distance(ptrace_registers.begin(), it); } return distance(registers.begin(), it); } int max_arg() { return arg_registers.size() - 1; } int arg_offset(int arg_num) { return offset(arg_registers.at(arg_num)); } #pragma GCC push_options #pragma GCC optimize("O0") static int *__getpc(void) { int *rtaddr; __asm__ volatile("move %0, $31" : "=r"(rtaddr)); return rtaddr; } #pragma GCC pop_options int pc_offset() { int *retAddr, pc; retAddr = __getpc(); pc = *retAddr; return pc; } int ret_offset() { return offset("v0"); } int sp_offset() { return offset("sp"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("mips64"); } std::vector invalid_watchpoint_modes() { throw std::runtime_error( "Watchpoints are not supported on this architecture"); } } // namespace arch } // namespace bpftrace bpftrace-0.14.0/src/arch/ppc64.cpp000066400000000000000000000045521413460502400165330ustar00rootroot00000000000000#include "arch.h" #include #include #include #include #define ARG_REGISTERS 8 // For little endian 64 bit, sp + 32 + 8 regs save area + argX #define ARG0_STACK_LE 96 // For big endian 64 bit, sp + 48 + 8 regs save area + argX #define ARG0_STACK_BE 112 namespace bpftrace { namespace arch { // clang-format off static std::vector> registers = { { "r0", "gpr[0]" }, { "r1", "gpr[1]" }, { "r2", "gpr[2]" }, { "r3", "gpr[3]" }, { "r4", "gpr[4]" }, { "r5", "gpr[5]" }, { "r6", "gpr[6]" }, { "r7", "gpr[7]" }, { "r8", "gpr[8]" }, { "r9", "gpr[9]" }, { "r10", "gpr[10]" }, { "r11", "gpr[11]" }, { "r12", "gpr[12]" }, { "r13", "gpr[13]" }, { "r14", "gpr[14]" }, { "r15", "gpr[15]" }, { "r16", "gpr[16]" }, { "r17", "gpr[17]" }, { "r18", "gpr[18]" }, { "r19", "gpr[19]" }, { "r20", "gpr[20]" }, { "r21", "gpr[21]" }, { "r22", "gpr[22]" }, { "r23", "gpr[23]" }, { "r24", "gpr[24]" }, { "r25", "gpr[25]" }, { "r26", "gpr[26]" }, { "r27", "gpr[27]" }, { "r28", "gpr[28]" }, { "r29", "gpr[29]" }, { "r30", "gpr[30]" }, { "r31", "gpr[31]" }, { "nip" }, { "msr" }, { "orig_gpr3" }, { "ctr" }, { "link" }, { "xer" }, { "ccr" }, { "softe" }, { "trap" }, { "dar" }, { "dsisr" }, { "result" }, }; static std::array arg_registers = { "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", }; // clang-format on int offset(std::string reg_name) { for (unsigned int i = 0; i < registers.size(); i++) { if (registers[i].count(reg_name)) return i; } return -1; } int max_arg() { return arg_registers.size() - 1; } int arg_offset(int arg_num) { return offset(arg_registers.at(arg_num)); } int ret_offset() { return offset("r3"); } int pc_offset() { return offset("nip"); } int sp_offset() { return offset("r1"); } int arg_stack_offset() { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return ARG0_STACK_LE / 8; #else return ARG0_STACK_BE / 8; #endif } std::string name() { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return std::string("ppc64le"); #else return std::string("ppc64"); #endif // __BYTE_ORDER__ } std::vector invalid_watchpoint_modes() { throw std::runtime_error( "Watchpoints are not supported on this architecture"); } } // namespace arch } // namespace bpftrace bpftrace-0.14.0/src/arch/s390.cpp000066400000000000000000000032731413460502400162740ustar00rootroot00000000000000#include "arch.h" #include #include #include #include #define ARG_REGISTERS 5 // For s390x, r2-r6 registers are used as function arguments, then the extra // arguments can be found starting at sp+160 #define ARG0_STACK 160 namespace bpftrace { namespace arch { // clang-format off static std::vector> registers = { // Breakpoint event address { "arg" }, { "pswmask" }, // Instruction address { "pswaddr" }, { "r0", "gprs[0]" }, { "r1", "gprs[1]" }, { "r2", "gprs[2]" }, { "r3", "gprs[3]" }, { "r4", "gprs[4]" }, { "r5", "gprs[5]" }, { "r6", "gprs[6]" }, { "r7", "gprs[7]" }, { "r8", "gprs[8]" }, { "r9", "gprs[9]" }, { "r10", "gprs[10]" }, { "r11", "gprs[11]" }, { "r12", "gprs[12]" }, { "r13", "gprs[13]" }, { "r14", "gprs[14]" }, { "r15", "gprs[15]" } }; static std::array arg_registers = { "r2", "r3", "r4", "r5", "r6", }; // clang-format on int offset(std::string reg_name) { for (unsigned int i = 0; i < registers.size(); i++) { if (registers[i].count(reg_name)) return i; } return -1; } int max_arg() { return arg_registers.size() - 1; } int arg_offset(int arg_num) { return offset(arg_registers.at(arg_num)); } int ret_offset() { return offset("r2"); } int pc_offset() { return offset("pswaddr"); } int sp_offset() { return offset("r15"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("s390x"); } std::vector invalid_watchpoint_modes() { throw std::runtime_error( "Watchpoints are not supported on this architecture"); } } // namespace arch } // namespace bpftrace bpftrace-0.14.0/src/arch/x86_64.cpp000066400000000000000000000025241413460502400165320ustar00rootroot00000000000000#include "arch.h" #include #include // SP + 8 points to the first argument that is passed on the stack #define ARG0_STACK 8 namespace bpftrace { namespace arch { // clang-format off static std::array registers = { "r15", "r14", "r13", "r12", "bp", "bx", "r11", "r10", "r9", "r8", "ax", "cx", "dx", "si", "di", "orig_ax", "ip", "cs", "flags", "sp", "ss", }; static std::array arg_registers = { "di", "si", "dx", "cx", "r8", "r9", }; // clang-format on int offset(std::string reg_name) { auto it = find(registers.begin(), registers.end(), reg_name); if (it == registers.end()) return -1; return distance(registers.begin(), it); } int max_arg() { return arg_registers.size() - 1; } int arg_offset(int arg_num) { return offset(arg_registers.at(arg_num)); } int ret_offset() { return offset("ax"); } int pc_offset() { return offset("ip"); } int sp_offset() { return offset("sp"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("x86_64"); } std::vector invalid_watchpoint_modes() { // See intel developer manual, Volume 3, section 17.2.4 return std::vector{ "r", "rx", "wx", "rwx", }; } } // namespace arch } // namespace bpftrace bpftrace-0.14.0/src/ast/000077500000000000000000000000001413460502400147375ustar00rootroot00000000000000bpftrace-0.14.0/src/ast/CMakeLists.txt000066400000000000000000000037161413460502400175060ustar00rootroot00000000000000add_library(ast_defs ast.cpp) add_library(ast async_event_types.cpp attachpoint_parser.cpp int_parser.cpp irbuilderbpf.cpp pass_manager.cpp signal.cpp visitors.cpp passes/field_analyser.cpp passes/portability_analyser.cpp passes/printer.cpp passes/resource_analyser.cpp passes/semantic_analyser.cpp passes/codegen_llvm.cpp ) target_include_directories(ast_defs PUBLIC ${CMAKE_SOURCE_DIR}/src) target_include_directories(ast_defs PUBLIC ${CMAKE_SOURCE_DIR}/src/ast) target_include_directories(ast_defs PUBLIC ${CMAKE_BINARY_DIR}) target_link_libraries(ast ast_defs arch bpforc parser) add_dependencies(ast_defs parser) target_compile_definitions(ast PRIVATE ${BPFTRACE_FLAGS}) if (STATIC_LINKING) set(clang_libs libclang.a clangAST clangAnalysis clangBasic clangDriver clangEdit clangFormat clangFrontend clangIndex clangLex clangParse clangRewrite clangSema clangSerialization clangToolingCore) set(llvm_lib_names bpfcodegen ipo irreader mcjit option orcjit ${LLVM_TARGETS_TO_BUILD}) if(EMBED_USE_LLVM) target_link_libraries(ast "-Wl,--start-group" ${CLANG_EMBEDDED_CMAKE_TARGETS} ${LLVM_EMBEDDED_CMAKE_TARGETS} "-Wl,--end-group") else() llvm_map_components_to_libnames(llvm_libs ${llvm_lib_names}) target_link_libraries(ast ${clang_libs}) target_link_libraries(ast ${llvm_libs}) endif() if(STATIC_LIBC) unlink_transitive_dependency("${CLANG_EXPORTED_TARGETS}" "LLVM") endif() else() find_library(found_LLVM LLVM HINTS ${LLVM_LIBRARY_DIRS}) if(found_LLVM) target_link_libraries(ast LLVM) else() llvm_map_components_to_libnames(_llvm_libs bpfcodegen ipo irreader mcjit orcjit ${LLVM_TARGETS_TO_BUILD}) llvm_expand_dependencies(llvm_libs ${_llvm_libs}) target_link_libraries(ast ${llvm_libs}) endif() target_link_libraries(ast libclang) endif() add_subdirectory(bpforc) bpftrace-0.14.0/src/ast/ast.cpp000066400000000000000000000307621413460502400162420ustar00rootroot00000000000000#include "ast.h" #include "log.h" #include "visitors.h" #include namespace bpftrace { namespace ast { #define MAKE_ACCEPT(Ty) \ void Ty::accept(VisitorBase &v) \ { \ v.visit(*this); \ }; MAKE_ACCEPT(Integer) MAKE_ACCEPT(String) MAKE_ACCEPT(StackMode) MAKE_ACCEPT(Builtin) MAKE_ACCEPT(Identifier) MAKE_ACCEPT(PositionalParameter) MAKE_ACCEPT(Call) MAKE_ACCEPT(Map) MAKE_ACCEPT(Variable) MAKE_ACCEPT(Binop) MAKE_ACCEPT(Unop) MAKE_ACCEPT(Ternary) MAKE_ACCEPT(FieldAccess) MAKE_ACCEPT(ArrayAccess) MAKE_ACCEPT(Cast) MAKE_ACCEPT(Tuple) MAKE_ACCEPT(ExprStatement) MAKE_ACCEPT(AssignMapStatement) MAKE_ACCEPT(AssignVarStatement) MAKE_ACCEPT(Predicate) MAKE_ACCEPT(AttachPoint) MAKE_ACCEPT(If) MAKE_ACCEPT(Unroll) MAKE_ACCEPT(While) MAKE_ACCEPT(Jump) MAKE_ACCEPT(Probe) MAKE_ACCEPT(Program) #undef MAKE_ACCEPT Call::~Call() { if (vargs) for (Expression *expr : *vargs) delete expr; delete vargs; vargs = nullptr; } Map::~Map() { if (vargs) for (Expression *expr : *vargs) delete expr; delete vargs; vargs = nullptr; } Binop::~Binop() { delete left; delete right; left = nullptr; right = nullptr; } Unop::~Unop() { delete expr; expr = nullptr; } FieldAccess::~FieldAccess() { delete expr; expr = nullptr; } ArrayAccess::~ArrayAccess() { delete expr; delete indexpr; expr = nullptr; indexpr = nullptr; } Cast::~Cast() { delete expr; expr = nullptr; } Tuple::~Tuple() { for (Expression *expr : *elems) delete expr; delete elems; } ExprStatement::~ExprStatement() { delete expr; expr = nullptr; } AssignMapStatement::~AssignMapStatement() { // In a compound assignment, the expression owns the map so // we shouldn't free if (!compound) delete map; delete expr; map = nullptr; expr = nullptr; } AssignVarStatement::~AssignVarStatement() { // In a compound assignment, the expression owns the map so // we shouldn't free if (!compound) delete var; delete expr; var = nullptr; expr = nullptr; } If::~If() { delete cond; cond = nullptr; if (stmts) for (Statement *s : *stmts) delete s; delete stmts; stmts = nullptr; if (else_stmts) for (Statement *s : *else_stmts) delete s; delete else_stmts; else_stmts = nullptr; } Unroll::~Unroll() { if (stmts) for (Statement *s : *stmts) delete s; delete stmts; stmts = nullptr; } Predicate::~Predicate() { delete expr; expr = nullptr; } Ternary::~Ternary() { delete cond; delete left; delete right; cond = nullptr; left = nullptr; right = nullptr; } While::~While() { delete cond; for (auto *stmt : *stmts) delete stmt; delete stmts; } Probe::~Probe() { if (attach_points) for (AttachPoint *ap : *attach_points) delete ap; delete attach_points; attach_points = nullptr; delete pred; pred = nullptr; if (stmts) for (Statement *s : *stmts) delete s; delete stmts; stmts = nullptr; } Program::~Program() { if (probes) for (Probe *p : *probes) delete p; delete probes; probes = nullptr; } Integer::Integer(long n, location loc) : Expression(loc), n(n) { is_literal = true; } String::String(const std::string &str, location loc) : Expression(loc), str(str) { is_literal = true; } StackMode::StackMode(const std::string &mode, location loc) : Expression(loc), mode(mode) { is_literal = true; } Builtin::Builtin(const std::string &ident, location loc) : Expression(loc), ident(is_deprecated(ident)) { } Identifier::Identifier(const std::string &ident, location loc) : Expression(loc), ident(ident) { } PositionalParameter::PositionalParameter(PositionalParameterType ptype, long n, location loc) : Expression(loc), ptype(ptype), n(n) { is_literal = true; } Call::Call(const std::string &func, location loc) : Expression(loc), func(is_deprecated(func)), vargs(nullptr) { } Call::Call(const std::string &func, ExpressionList *vargs, location loc) : Expression(loc), func(is_deprecated(func)), vargs(vargs) { } Map::Map(const std::string &ident, location loc) : Expression(loc), ident(ident), vargs(nullptr) { is_map = true; } Map::Map(const std::string &ident, ExpressionList *vargs, location loc) : Expression(loc), ident(ident), vargs(vargs) { is_map = true; for (auto expr : *vargs) { expr->key_for_map = this; } } Variable::Variable(const std::string &ident, location loc) : Expression(loc), ident(ident) { is_variable = true; } Binop::Binop(Expression *left, Operator op, Expression *right, location loc) : Expression(loc), left(left), right(right), op(op) { } Unop::Unop(Operator op, Expression *expr, location loc) : Expression(loc), expr(expr), op(op), is_post_op(false) { } Unop::Unop(Operator op, Expression *expr, bool is_post_op, location loc) : Expression(loc), expr(expr), op(op), is_post_op(is_post_op) { } Ternary::Ternary(Expression *cond, Expression *left, Expression *right, location loc) : Expression(loc), cond(cond), left(left), right(right) { } FieldAccess::FieldAccess(Expression *expr, const std::string &field, location loc) : Expression(loc), expr(expr), field(field) { } FieldAccess::FieldAccess(Expression *expr, ssize_t index, location loc) : Expression(loc), expr(expr), index(index) { } ArrayAccess::ArrayAccess(Expression *expr, Expression *indexpr, location loc) : Expression(loc), expr(expr), indexpr(indexpr) { } Cast::Cast(const std::string &type, bool is_pointer, bool is_double_pointer, Expression *expr, location loc) : Expression(loc), cast_type(type), is_pointer(is_pointer), is_double_pointer(is_double_pointer), expr(expr) { } Tuple::Tuple(ExpressionList *elems, location loc) : Expression(loc), elems(elems) { } ExprStatement::ExprStatement(Expression *expr, location loc) : Statement(loc), expr(expr) { } AssignMapStatement::AssignMapStatement(Map *map, Expression *expr, bool compound, location loc) : Statement(loc), map(map), expr(expr), compound(compound) { expr->map = map; }; AssignVarStatement::AssignVarStatement(Variable *var, Expression *expr, bool compound, location loc) : Statement(loc), var(var), expr(expr), compound(compound) { expr->var = var; } Predicate::Predicate(Expression *expr, location loc) : Node(loc), expr(expr) { } AttachPoint::AttachPoint(const std::string &raw_input, location loc) : Node(loc), raw_input(raw_input) { } If::If(Expression *cond, StatementList *stmts) : cond(cond), stmts(stmts) { } If::If(Expression *cond, StatementList *stmts, StatementList *else_stmts) : cond(cond), stmts(stmts), else_stmts(else_stmts) { } Unroll::Unroll(Expression *expr, StatementList *stmts, location loc) : Statement(loc), expr(expr), stmts(stmts) { } Probe::Probe(AttachPointList *attach_points, Predicate *pred, StatementList *stmts) : attach_points(attach_points), pred(pred), stmts(stmts) { } Program::Program(const std::string &c_definitions, ProbeList *probes) : c_definitions(c_definitions), probes(probes) { } std::string opstr(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(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(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: return "++"; case Operator::DECREMENT: return "--"; default: return {}; } return {}; // unreached } std::string AttachPoint::name(const std::string &attach_target, const std::string &attach_point) const { std::string n = provider; if (attach_target != "") n += ":" + attach_target; if (ns != "") n += ":" + ns; if (attach_point != "") { n += ":" + attach_point; if (func_offset != 0) n += "+" + std::to_string(func_offset); } if (address != 0) n += ":" + std::to_string(address); if (freq != 0) n += ":" + std::to_string(freq); if (len != 0) n += ":" + std::to_string(len); if (mode.size()) n += ":" + mode; return n; } std::string AttachPoint::name(const std::string &attach_point) const { return name(target, attach_point); } int AttachPoint::index(const std::string &name) const { if (index_.count(name) == 0) return 0; return index_.at(name); } void AttachPoint::set_index(const std::string &name, int index) { index_[name] = index; } std::string Probe::name() const { std::string n; for (auto &attach_point : *attach_points) { if (!n.empty()) n += ','; n += attach_point->provider; if (attach_point->target != "") n += ":" + attach_point->target; if (attach_point->ns != "") n += ":" + attach_point->ns; if (attach_point->func != "") { n += ":" + attach_point->func; if (attach_point->func_offset != 0) n += "+" + std::to_string(attach_point->func_offset); } if (attach_point->address != 0) n += ":" + std::to_string(attach_point->address); if (attach_point->freq != 0) n += ":" + std::to_string(attach_point->freq); } return n; } int Probe::index() const { return index_; } void Probe::set_index(int index) { index_ = index; } Expression::Expression(const Expression &other) : Node(other) { type = other.type; is_literal = other.is_literal; is_variable = other.is_variable; is_map = other.is_map; } Call::Call(const Call &other) : Expression(other) { func = other.func; } Binop::Binop(const Binop &other) : Expression(other) { op = other.op; } Unop::Unop(const Unop &other) : Expression(other) { op = other.op; is_post_op = other.is_post_op; } Map::Map(const Map &other) : Expression(other) { ident = other.ident; skip_key_validation = other.skip_key_validation; } FieldAccess::FieldAccess(const FieldAccess &other) : Expression(other), expr(nullptr) { field = other.field; index = other.index; } Unroll::Unroll(const Unroll &other) : Statement(other) { var = other.var; } Program::Program(const Program &other) : Node(other) { c_definitions = other.c_definitions; } Cast::Cast(const Cast &other) : Expression(other) { cast_type = other.cast_type; is_pointer = other.is_pointer; is_double_pointer = other.is_double_pointer; } Probe::Probe(const Probe &other) : Node(other) { need_expansion = other.need_expansion; tp_args_structs_level = other.tp_args_structs_level; index_ = other.index_; } While::While(const While &other) : Statement(other) { } Tuple::Tuple(const Tuple &other) : Expression(other) { } If::If(const If &other) : Statement(other) { } AssignVarStatement::AssignVarStatement(const AssignVarStatement &other) : Statement(other) { compound = other.compound; }; AssignMapStatement::AssignMapStatement(const AssignMapStatement &other) : Statement(other) { compound = other.compound; }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/ast.h000066400000000000000000000307011413460502400157000ustar00rootroot00000000000000#pragma once #include "location.hh" #include "utils.h" #include #include #include #include "mapkey.h" #include "types.h" #include "usdt.h" namespace bpftrace { namespace ast { class VisitorBase; #define DEFINE_ACCEPT void accept(VisitorBase &v) override; /** * Copy the node but leave all it's child members uninitialized, effecitvely * turning it into a leaf node */ #define DEFINE_LEAFCOPY(T) \ T *leafcopy() \ { \ return new T(*this); \ }; 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() = default; Node(location loc) : loc(loc){}; Node(const Node &other) = default; Node &operator=(const Node &) = delete; Node(Node &&) = delete; Node &operator=(Node &&) = delete; virtual ~Node() = default; virtual void accept(VisitorBase &v) = 0; location loc; }; class Map; class Variable; class Expression : public Node { public: Expression() = default; Expression(location loc) : Node(loc){}; Expression(const Expression &other); Expression &operator=(const Expression &) = delete; Expression(Expression &&) = delete; Expression &operator=(Expression &&) = delete; // NB: do not free any of non-owned pointers we store virtual ~Expression() = default; SizedType type; Map *key_for_map = nullptr; Map *map = nullptr; // Only set when this expression is assigned to a map Variable *var = nullptr; // Set when this expression is assigned to a variable bool is_literal = false; bool is_variable = false; bool is_map = false; }; using ExpressionList = std::vector; class Integer : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Integer) explicit Integer(long n, location loc); long n; private: Integer(const Integer &other) = default; }; class PositionalParameter : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(PositionalParameter) explicit PositionalParameter(PositionalParameterType ptype, long n, location loc); ~PositionalParameter() = default; PositionalParameterType ptype; long n; bool is_in_str = false; private: PositionalParameter(const PositionalParameter &other) = default; }; class String : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(String) explicit String(const std::string &str, location loc); ~String() = default; std::string str; private: String(const String &other) = default; }; class StackMode : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(StackMode) explicit StackMode(const std::string &mode, location loc); ~StackMode() = default; std::string mode; private: StackMode(const StackMode &other) = default; }; class Identifier : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Identifier) explicit Identifier(const std::string &ident, location loc); ~Identifier() = default; std::string ident; private: Identifier(const Identifier &other) = default; }; class Builtin : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Builtin) explicit Builtin(const std::string &ident, location loc); ~Builtin() = default; std::string ident; int probe_id; private: Builtin(const Builtin &other) = default; }; class Call : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Call) explicit Call(const std::string &func, location loc); Call(const std::string &func, ExpressionList *vargs, location loc); ~Call(); std::string func; ExpressionList *vargs = nullptr; private: Call(const Call &other); }; class Map : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Map) explicit Map(const std::string &ident, location loc); Map(const std::string &ident, ExpressionList *vargs, location loc); ~Map(); std::string ident; MapKey key_type; ExpressionList *vargs = nullptr; bool skip_key_validation = false; private: Map(const Map &other); }; class Variable : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Variable) explicit Variable(const std::string &ident, location loc); ~Variable() = default; std::string ident; private: Variable(const Variable &other) = default; }; class Binop : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Binop) Binop(Expression *left, Operator op, Expression *right, location loc); ~Binop(); Expression *left = nullptr; Expression *right = nullptr; Operator op; private: Binop(const Binop &other); }; class Unop : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Unop) Unop(Operator op, Expression *expr, location loc = location()); Unop(Operator op, Expression *expr, bool is_post_op = false, location loc = location()); ~Unop(); Expression *expr = nullptr; Operator op; bool is_post_op; private: Unop(const Unop &other); }; class FieldAccess : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(FieldAccess) FieldAccess(Expression *expr, const std::string &field); FieldAccess(Expression *expr, const std::string &field, location loc); FieldAccess(Expression *expr, ssize_t index, location loc); ~FieldAccess(); Expression *expr = nullptr; std::string field; ssize_t index = -1; private: FieldAccess(const FieldAccess &other); }; class ArrayAccess : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(ArrayAccess) ArrayAccess(Expression *expr, Expression *indexpr); ArrayAccess(Expression *expr, Expression *indexpr, location loc); ~ArrayAccess(); Expression *expr = nullptr; Expression *indexpr = nullptr; private: ArrayAccess(const ArrayAccess &other) : Expression(other){}; }; class Cast : public Expression { public: DEFINE_LEAFCOPY(Cast) DEFINE_ACCEPT Cast(const std::string &type, bool is_pointer, bool is_double_pointer, Expression *expr); Cast(const std::string &type, bool is_pointer, bool is_double_pointer, Expression *expr, location loc); ~Cast(); std::string cast_type; bool is_pointer; bool is_double_pointer; Expression *expr = nullptr; private: Cast(const Cast &other); }; class Tuple : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Tuple) Tuple(ExpressionList *elems, location loc); ~Tuple(); ExpressionList *elems = nullptr; private: Tuple(const Tuple &other); }; class Statement : public Node { public: Statement() = default; Statement(location loc) : Node(loc){}; Statement(const Statement &other) = default; ~Statement() = default; Statement &operator=(const Statement &) = delete; Statement(Statement &&) = delete; Statement &operator=(Statement &&) = delete; }; using StatementList = std::vector; class ExprStatement : public Statement { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(ExprStatement) explicit ExprStatement(Expression *expr, location loc); ~ExprStatement(); Expression *expr = nullptr; private: ExprStatement(const ExprStatement &other) : Statement(other){}; }; class AssignMapStatement : public Statement { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(AssignMapStatement) AssignMapStatement(Map *map, Expression *expr, bool compound = false, location loc = location()); ~AssignMapStatement(); Map *map = nullptr; Expression *expr = nullptr; bool compound; private: AssignMapStatement(const AssignMapStatement &other); }; class AssignVarStatement : public Statement { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(AssignVarStatement) AssignVarStatement(Variable *var, Expression *expr, bool compound = false, location loc = location()); ~AssignVarStatement(); Variable *var = nullptr; Expression *expr = nullptr; bool compound; private: AssignVarStatement(const AssignVarStatement &other); }; class If : public Statement { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(If) If(Expression *cond, StatementList *stmts); If(Expression *cond, StatementList *stmts, StatementList *else_stmts); ~If(); Expression *cond = nullptr; StatementList *stmts = nullptr; StatementList *else_stmts = nullptr; private: If(const If &other); }; class Unroll : public Statement { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Unroll) Unroll(Expression *expr, StatementList *stmts, location loc); ~Unroll(); long int var = 0; Expression *expr = nullptr; StatementList *stmts = nullptr; private: Unroll(const Unroll &other); }; class Jump : public Statement { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Jump) Jump(JumpType ident, location loc = location()) : Statement(loc), ident(ident) { } ~Jump() = default; JumpType ident = JumpType::INVALID; private: Jump(const Jump &other) = default; }; class Predicate : public Node { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Predicate) explicit Predicate(Expression *expr, location loc); ~Predicate(); Expression *expr = nullptr; private: Predicate(const Predicate &other) : Node(other){}; }; class Ternary : public Expression { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Ternary) Ternary(Expression *cond, Expression *left, Expression *right, location loc); ~Ternary(); Expression *cond = nullptr; Expression *left = nullptr; Expression *right = nullptr; }; class While : public Statement { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(While) While(Expression *cond, StatementList *stmts, location loc) : Statement(loc), cond(cond), stmts(stmts) { } ~While(); Expression *cond = nullptr; StatementList *stmts = nullptr; private: While(const While &other); }; class AttachPoint : public Node { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(AttachPoint) explicit AttachPoint(const std::string &raw_input, location loc = location()); AttachPoint(const std::string &raw_input, bool ignore_invalid) : AttachPoint(raw_input) { this->ignore_invalid = ignore_invalid; } ~AttachPoint() = default; // Raw, unparsed input from user, eg. kprobe:vfs_read std::string raw_input; std::string provider; std::string target; 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 bool need_expansion = false; uint64_t address = 0; uint64_t func_offset = 0; bool ignore_invalid = false; std::string name(const std::string &attach_point) const; std::string name(const std::string &attach_target, const std::string &attach_point) const; int index(const std::string &name) const; void set_index(const std::string &name, int index); private: AttachPoint(const AttachPoint &other) = default; std::map index_; }; using AttachPointList = std::vector; class Probe : public Node { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Probe) Probe(AttachPointList *attach_points, Predicate *pred, StatementList *stmts); ~Probe(); AttachPointList *attach_points = nullptr; Predicate *pred = nullptr; StatementList *stmts = nullptr; std::string name() const; bool need_expansion = false; // must build a BPF program per wildcard match int tp_args_structs_level = -1; // number of levels of structs that must // be imported/resolved for tracepoints int index() const; void set_index(int index); private: Probe(const Probe &other); int index_ = 0; }; using ProbeList = std::vector; class Program : public Node { public: DEFINE_ACCEPT DEFINE_LEAFCOPY(Program) Program(const std::string &c_definitions, ProbeList *probes); ~Program(); std::string c_definitions; ProbeList *probes = nullptr; private: Program(const Program &other); }; std::string opstr(Binop &binop); std::string opstr(Unop &unop); std::string opstr(Jump &jump); #undef DEFINE_ACCEPT } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/async_event_types.cpp000066400000000000000000000037101413460502400212060ustar00rootroot00000000000000#include "async_event_types.h" #include #include #include "irbuilderbpf.h" namespace bpftrace { namespace 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.getInt64Ty(), // strftime id b.getInt64Ty(), // strftime arg, time elapsed since boot }; } std::vector Buf::asLLVMType(ast::IRBuilderBPF& b, size_t length) { return { b.getInt8Ty(), // buffer length llvm::ArrayType::get(b.getInt8Ty(), length), // buffer content }; } std::vector HelperError::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // error_id b.getInt32Ty(), // return value }; } std::vector Watchpoint::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // watchpoint_idx b.getInt64Ty(), // addr }; } std::vector WatchpointUnwatch::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // addr }; } } // namespace AsyncEvent } // namespace bpftrace bpftrace-0.14.0/src/ast/async_event_types.h000066400000000000000000000047761413460502400206700ustar00rootroot00000000000000#pragma once #include #include #include // Forward declare some LLVM types here. We do not #include any LLVM // headers b/c that will pull in LLVM's ABI checking machinery. We want // this header to be independent of LLVM during link time so that AOT // does not need to link against LLVM. namespace llvm { class Type; } // namespace llvm namespace bpftrace { namespace ast { class IRBuilderBPF; } // namespace ast } // namespace bpftrace /* * The main goal here is to keep the struct definitions close to each other, * making it easier to spot type mismatches. * * If you update a type, remember to update the .cpp too! */ namespace bpftrace { namespace AsyncEvent { struct Print { uint64_t action_id; uint32_t mapid; uint32_t top; uint32_t div; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct PrintNonMap { uint64_t action_id; uint64_t print_id; // See below why we don't use a flexible length array uint8_t content[0]; std::vector asLLVMType(ast::IRBuilderBPF& b, size_t size); } __attribute__((packed)); struct MapEvent { uint64_t action_id; uint32_t mapid; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Time { uint64_t action_id; uint32_t time_id; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Strftime { uint64_t strftime_id; uint64_t nsecs_since_boot; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Buf { uint8_t length; // Seems like GCC 7.4.x can't handle `char content[]`. Work around by using // 0 sized array (a GCC extension that clang also accepts: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70932). It also looks like // the issue doesn't exist in GCC 7.5.x. char content[0]; std::vector asLLVMType(ast::IRBuilderBPF& b, size_t length); } __attribute__((packed)); struct HelperError { uint64_t action_id; uint64_t error_id; int32_t return_value; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Watchpoint { uint64_t action_id; uint64_t watchpoint_idx; uint64_t addr; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct WatchpointUnwatch { uint64_t action_id; uint64_t addr; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); } // namespace AsyncEvent } // namespace bpftrace bpftrace-0.14.0/src/ast/attachpoint_parser.cpp000066400000000000000000000412341413460502400213410ustar00rootroot00000000000000#include "attachpoint_parser.h" #include #include #include #include #include #include #include "int_parser.h" #include "log.h" #include "types.h" #ifdef HAVE_BCC_WHICH_SO #include #endif namespace bpftrace { namespace ast { std::optional AttachPointParser::stoull(const std::string &str) { try { return int_parser::to_uint(str, 0); } catch (const std::exception &e) { errs_ << e.what(); return std::nullopt; } } std::optional AttachPointParser::stoll(const std::string &str) { try { return int_parser::to_uint(str, 0); } catch (const std::exception &e) { errs_ << e.what(); return std::nullopt; } } AttachPointParser::AttachPointParser(Program *root, BPFtrace &bpftrace, std::ostream &sink, bool listing) : root_(root), bpftrace_(bpftrace), sink_(sink), listing_(listing) { } int AttachPointParser::parse() { if (!root_) return 1; uint32_t failed = 0; for (Probe *probe : *(root_->probes)) { for (size_t i = 0; i < probe->attach_points->size(); ++i) { auto &ap = *(*probe->attach_points)[i]; new_attach_points.clear(); State s = parse_attachpoint(ap); if (s == INVALID) { ++failed; LOG(ERROR, ap.loc, sink_) << errs_.str(); errs_.str({}); // clear buffer } 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()); } } } } 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; } std::set probe_types; if (has_wildcard(parts_.front())) { // Single argument listing looks at all relevant probe types std::string probetype_query = (parts_.size() == 1) ? "*" : parts_.front(); // Probe type expansion // If PID is specified or the second part of the attach point is a path // (contains '/'), use userspace probe types. // Otherwise, use kernel probe types. if (bpftrace_.pid() > 0 || (parts_.size() >= 2 && parts_[1].find('/') != std::string::npos)) { probe_types = bpftrace_.probe_matcher_->expand_probetype_userspace( probetype_query); } else { probe_types = bpftrace_.probe_matcher_->expand_probetype_kernel( probetype_query); } } else probe_types = { parts_.front() }; if (probe_types.empty()) { if (has_wildcard(parts_.front())) errs_ << "No probe type matched for " << parts_.front() << std::endl; else errs_ << "Invalid probe type: " << parts_.front() << std::endl; return INVALID; } else if (probe_types.size() > 1) { // If the probe type string matches more than 1 probe, create a new set of // attach points (one for every match) that will replace the original one. for (const auto &probe_type : probe_types) { std::string raw_input = ap.raw_input; if (parts_.size() > 1) erase_prefix(raw_input); raw_input = probe_type + ":" + raw_input; // New attach points have ignore_invalid set to true - probe types for // which raw_input has invalid number of parts will be ignored (instead // of throwing an error) new_attach_points.push_back(new AttachPoint(raw_input, true)); } return NEW_APS; } ap_->provider = probetypeName(*probe_types.begin()); switch (probetype(ap.provider)) { 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::kfunc: case ProbeType::kretfunc: return kfunc_parser(); case ProbeType::iter: return iter_parser(); default: errs_ << "Unrecognized probe type: " << ap_->provider << std::endl; return INVALID; } return OK; } AttachPointParser::State AttachPointParser::lex_attachpoint( const AttachPoint &ap) { const auto &raw = ap.raw_input; std::vector ret; bool in_quotes = false; std::string argument; for (size_t idx = 0; idx < raw.size(); ++idx) { if (raw[idx] == ':' && !in_quotes) { parts_.emplace_back(std::move(argument)); // The standard says an std::string in moved-from state is in // valid but unspecified state, so clear() to be safe argument.clear(); } else if (raw[idx] == '"') in_quotes = !in_quotes; // Handle escaped characters in a string else if (in_quotes && raw[idx] == '\\' && (idx + 1 < raw.size())) { argument += raw[idx + 1]; ++idx; } else if (!in_quotes && raw[idx] == '$') { // There's an assumption that the positional paramter is well // formed. ie we are not expecting a bare `$` or `$nonint`. The // bison parser should have guaranteed this. size_t i = idx + 1; size_t len = 0; while (i < raw.size() && (raw[i] != '"' && raw[i] != ':')) { len++; i++; } std::string param_idx_str = raw.substr(idx + 1, len); size_t pos, param_idx; param_idx = std::stoll(param_idx_str, &pos, 0); if (pos != param_idx_str.size()) { errs_ << "Found trailing text '" << param_idx_str.substr(pos) << "' in positional parameter index. Try quoting the trailing text." << std::endl; return State::INVALID; } argument += bpftrace_.get_param(param_idx, true); // NB the for loop will then do idx++ so while we consumed // (len + 1) characters, we only increment by (len). idx += len; } 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::kprobe_parser(bool allow_offset) { if (parts_.size() != 2) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type requires 1 argument" << std::endl; return INVALID; } // Handle kprobe:func+0x100 case auto plus_count = std::count(parts_[1].cbegin(), parts_[1].cend(), '+'); if (plus_count) { if (!allow_offset) { errs_ << "Offset not allowed" << std::endl; return INVALID; } if (plus_count != 1) { errs_ << "Cannot take more than one offset" << std::endl; return INVALID; } auto offset_parts = split_string(parts_[1], '+', true); if (offset_parts.size() != 2) { errs_ << "Invalid offset" << std::endl; return INVALID; } ap_->func = offset_parts[0]; auto res = stoll(offset_parts[1]); if (res) { ap_->func_offset = *res; } else { return INVALID; } } // Default case (eg kprobe:func) else { ap_->func = parts_[1]; } if (ap_->func.find('*') != std::string::npos) ap_->need_expansion = true; return OK; } AttachPointParser::State AttachPointParser::kretprobe_parser() { return kprobe_parser(false); } AttachPointParser::State AttachPointParser::uprobe_parser(bool allow_offset, bool allow_abs_addr) { // Handle special probes implemented as uprobes if (ap_->provider == "BEGIN" || ap_->provider == "END") { if (parts_.size() == 2 && parts_[1] == "*") parts_.pop_back(); if (parts_.size() != 1) { errs_ << ap_->provider << " probe type requires 0 arguments" << std::endl; return INVALID; } return OK; } // Now handle regular uprobes if (parts_.size() == 2 && bpftrace_.pid() > 0) { // For PID, the target may be skipped parts_.push_back(parts_[1]); parts_[1] = ""; } if (parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type requires 2 arguments" << std::endl; return INVALID; } ap_->target = ""; #ifdef HAVE_BCC_WHICH_SO if (!has_wildcard(parts_[1]) && parts_[1].find("lib") == 0) { // Automatic resolution of shared library paths. // If the target has form "libXXX" then we use BCC to find the correct path // to the given library as it may differ across systems. auto libname = parts_[1].substr(3); const char *lib_path = bcc_procutils_which_so(libname.c_str(), bpftrace_.pid()); if (lib_path) ap_->target = lib_path; } #endif if (ap_->target.empty()) { if (bpftrace_.pid() > 0) { ap_->target = get_pid_exe(bpftrace_.pid()); ap_->target = path_for_pid_mountns(bpftrace_.pid(), ap_->target); } else ap_->target = parts_[1]; } // Handle uprobe:/lib/asdf:func+0x100 case auto plus_count = std::count(parts_[2].cbegin(), parts_[2].cend(), '+'); if (plus_count) { if (!allow_offset) { errs_ << "Offset not allowed" << std::endl; return INVALID; } if (plus_count != 1) { errs_ << "Cannot take more than one offset" << std::endl; return INVALID; } auto offset_parts = split_string(parts_[2], '+', true); if (offset_parts.size() != 2) { errs_ << "Invalid offset" << std::endl; return INVALID; } ap_->func = offset_parts[0]; auto res = stoll(offset_parts[1]); if (res) { ap_->func_offset = *res; } else { return INVALID; } } // Default case (eg uprobe:[addr][func]) else { if (allow_abs_addr) { auto res = stoll(parts_[2]); if (res) { ap_->address = *res; } else { ap_->address = 0; ap_->func = parts_[2]; } } else ap_->func = parts_[2]; } if (ap_->target.find('*') != std::string::npos || ap_->func.find('*') != std::string::npos) ap_->need_expansion = true; return OK; } AttachPointParser::State AttachPointParser::uretprobe_parser() { return uprobe_parser(false); } AttachPointParser::State AttachPointParser::usdt_parser() { if (bpftrace_.pid() > 0) { // For PID, the target can be skipped if (parts_.size() == 2) { parts_.push_back(parts_[1]); parts_[1] = ""; } } if (parts_.size() != 3 && parts_.size() != 4) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type requires 2 or 3 arguments" << std::endl; return INVALID; } 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]; } if (ap_->target.find('*') != std::string::npos || ap_->ns.find('*') != std::string::npos || ap_->ns.empty() || ap_->func.find('*') != std::string::npos || bpftrace_.pid()) ap_->need_expansion = true; return OK; } AttachPointParser::State AttachPointParser::tracepoint_parser() { // Help with `bpftrace -l 'tracepoint:*foo*'` listing -- wildcard the // tracepoint category b/c user is most likely to be looking for the event // name if (parts_.size() == 2 && has_wildcard(parts_.at(1))) parts_.insert(parts_.begin() + 1, "*"); if (parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type requires 2 arguments" << std::endl; return INVALID; } ap_->target = parts_[1]; ap_->func = parts_[2]; if (ap_->target.find('*') != std::string::npos || ap_->func.find('*') != std::string::npos) ap_->need_expansion = true; return OK; } AttachPointParser::State AttachPointParser::profile_parser() { if (parts_.size() != 3) { errs_ << ap_->provider << " probe type requires 2 arguments" << std::endl; return INVALID; } ap_->target = parts_[1]; auto res = stoull(parts_[2]); if (res) ap_->freq = *res; else return INVALID; return OK; } AttachPointParser::State AttachPointParser::interval_parser() { if (parts_.size() != 3) { errs_ << ap_->provider << " probe type requires 2 arguments" << std::endl; return INVALID; } ap_->target = parts_[1]; auto res = stoull(parts_[2]); if (res) ap_->freq = *res; else return INVALID; return OK; } AttachPointParser::State AttachPointParser::software_parser() { if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type requires 1 or 2 arguments" << std::endl; return INVALID; } ap_->target = parts_[1]; if (parts_.size() == 3 && parts_[2] != "*") { auto res = stoull(parts_[2]); if (res) ap_->freq = *res; else return INVALID; } return OK; } AttachPointParser::State AttachPointParser::hardware_parser() { if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type requires 1 or 2 arguments" << std::endl; return INVALID; } ap_->target = parts_[1]; if (parts_.size() == 3 && parts_[2] != "*") { auto res = stoull(parts_[2]); if (res) ap_->freq = *res; else return INVALID; } return OK; } AttachPointParser::State AttachPointParser::watchpoint_parser(bool async) { if (parts_.size() != 4) { errs_ << ap_->provider << " probe type requires 3 arguments" << std::endl; return INVALID; } if (parts_[1].find('+') == std::string::npos) { auto parsed = stoull(parts_[1]); if (parsed) ap_->address = *parsed; else return INVALID; } else { auto func_arg_parts = split_string(parts_[1], '+', true); if (func_arg_parts.size() != 2) { errs_ << "Invalid function/address argument" << std::endl; return INVALID; } ap_->func = func_arg_parts[0]; if (ap_->func.find('*') != std::string::npos) ap_->need_expansion = true; if (func_arg_parts[1].size() <= 3 || func_arg_parts[1].find("arg") != 0) { errs_ << "Invalid function argument" << std::endl; return INVALID; } auto parsed = stoull(func_arg_parts[1].substr(3)); if (parsed) ap_->address = *parsed; else return INVALID; } auto len_parsed = stoull(parts_[2]); if (len_parsed) ap_->len = *len_parsed; else return INVALID; // 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::kfunc_parser() { if (parts_.size() != 2) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type requires 1 argument" << std::endl; return INVALID; } if (parts_[1].find('*') != std::string::npos) ap_->need_expansion = true; ap_->func = parts_[1]; return OK; } AttachPointParser::State AttachPointParser::iter_parser() { if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type takes 2 arguments (1 optional)" << std::endl; return INVALID; } if (parts_[1].find('*') != std::string::npos) { if (listing_) { ap_->need_expansion = true; } else { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type does not support wildcards" << std::endl; return INVALID; } } ap_->func = parts_[1]; if (parts_.size() == 3) ap_->pin = parts_[2]; return OK; } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/attachpoint_parser.h000066400000000000000000000035771413460502400210160ustar00rootroot00000000000000#pragma once #include #include #include #include "ast.h" #include "bpftrace.h" namespace bpftrace { namespace ast { class AttachPointParser { public: AttachPointParser(Program *root, BPFtrace &bpftrace, std::ostream &sink, bool listing); ~AttachPointParser() = default; int parse(); private: enum State { OK = 0, INVALID, NEW_APS, SKIP }; State parse_attachpoint(AttachPoint &ap); /* * This method splits an attach point definition into arguments, * where arguments are separated by `:`. The exception is `:`s inside * of quoted strings, which we must treat as a literal. * * This method also resolves positional parameters. Positional params * may be escaped with double quotes. * * Note that this function assumes the raw string is generally well * formed. More specifically, that there is no unescaped whitespace * and no unmatched quotes. */ State lex_attachpoint(const AttachPoint &ap); State 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 kfunc_parser(); State iter_parser(); std::optional stoull(const std::string &str); std::optional stoll(const std::string &str); Program *root_{ nullptr }; // Non-owning pointer BPFtrace &bpftrace_; std::ostream &sink_; AttachPoint *ap_{ nullptr }; // Non-owning pointer std::stringstream errs_; std::vector parts_; AttachPointList new_attach_points; bool listing_; }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/bpforc/000077500000000000000000000000001413460502400162125ustar00rootroot00000000000000bpftrace-0.14.0/src/ast/bpforc/CMakeLists.txt000066400000000000000000000000371413460502400207520ustar00rootroot00000000000000add_library(bpforc bpforc.cpp) bpftrace-0.14.0/src/ast/bpforc/bpforc.cpp000066400000000000000000000034641413460502400202000ustar00rootroot00000000000000#include "bpforc.h" namespace bpftrace { using namespace llvm; using namespace llvm::orc; uint8_t *MemoryManager::allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) { uint8_t *addr = SectionMemoryManager::allocateCodeSection( Size, Alignment, SectionID, SectionName); sections_[SectionName.str()] = std::make_tuple(addr, Size); return addr; } uint8_t *MemoryManager::allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, bool isReadOnly) { uint8_t *addr = SectionMemoryManager::allocateDataSection( Size, Alignment, SectionID, SectionName, isReadOnly); sections_[SectionName.str()] = std::make_tuple(addr, Size); return addr; } std::unordered_map> BpfOrc::getBytecode() const { std::unordered_map> ret; for (const auto &[section, bytecode_tuple] : sections_) { std::vector section_bytecode; const auto &[ptr, size] = bytecode_tuple; section_bytecode.reserve(size); section_bytecode.assign(ptr, ptr + size); ret.insert({ section, section_bytecode }); } return ret; } std::optional> BpfOrc::getSection( const std::string &name) { auto sec = sections_.find(name); if (sec == sections_.end()) return std::nullopt; return sec->second; } #ifdef LLVM_ORC_V1 #include "bpforcv1.cpp" #else // LLVM_ORC_V2 #include "bpforcv2.cpp" #endif } // namespace bpftrace bpftrace-0.14.0/src/ast/bpforc/bpforc.h000066400000000000000000000102001413460502400176270ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #if LLVM_VERSION_MAJOR == 6 #include #endif #include #include #include #if LLVM_VERSION_MAJOR < 14 #include #else #include #endif #include #ifdef LLVM_ORC_V2 #include #include #if LLVM_VERSION_MAJOR >= 13 #include #endif #endif #include #include namespace bpftrace { const std::string LLVMTargetTriple = "bpf-pc-linux"; using namespace llvm; using namespace llvm::orc; /* Custom memory manager to keep track of the address and size of code sections created. Each section being a Probe. */ // name -> {addr, size} using SectionMap = std::unordered_map>; class MemoryManager : public SectionMemoryManager { public: explicit MemoryManager(SectionMap §ions) : sections_(sections) { } uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) override; uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, bool isReadOnly) override; ~MemoryManager() { deregisterEHFrames(); }; private: SectionMap §ions_; }; class BpfOrc { private: SectionMap sections_; std::unique_ptr TM; DataLayout DL; #if LLVM_VERSION_MAJOR >= 7 #ifdef LLVM_ORC_V2 std::unique_ptr ES; #else // LLVM_ORC_V1 ExecutionSession ES; #endif #endif #if LLVM_VERSION_MAJOR >= 7 && LLVM_VERSION_MAJOR < 12 std::shared_ptr Resolver; #endif #ifdef LLVM_ORC_V2 RTDyldObjectLinkingLayer ObjectLayer; IRCompileLayer CompileLayer; MangleAndInterner Mangle; ThreadSafeContext CTX; JITDylib &MainJD; #else // LLVM_ORC_V1 #if LLVM_VERSION_MAJOR >= 8 LLVMContext CTX; LegacyRTDyldObjectLinkingLayer ObjectLayer; LegacyIRCompileLayer CompileLayer; #else LLVMContext CTX; RTDyldObjectLinkingLayer ObjectLayer; IRCompileLayer CompileLayer; #endif #endif public: #if LLVM_VERSION_MAJOR >= 13 ~BpfOrc() { if (auto Err = ES->endSession()) ES->reportError(std::move(Err)); } #endif #ifdef LLVM_ORC_V2 BpfOrc(TargetMachine *TM, DataLayout DL, std::unique_ptr ES); #else BpfOrc(TargetMachine *TM, DataLayout DL); #endif void compile(std::unique_ptr M); /* Helper for creating a orc object, responsible for creating internal objects */ static std::unique_ptr Create(); // Get an owned copy of generated bytecode std::unordered_map> getBytecode() const; /* get the {addr, size} of a code section */ std::optional> getSection( const std::string &name); LLVMContext &getContext(); const DataLayout &getDataLayout() const { return DL; } TargetMachine &getTargetMachine() { return *TM; } /* Dump the JIT state, only works for ORCv2 */ void dump([[maybe_unused]] raw_ostream &os) { #ifdef LLVM_ORC_V2 MainJD.dump(os); #endif } #ifdef LLVM_ORC_V2 Expected lookup(StringRef Name) { return ES->lookup({ &MainJD }, Mangle(Name.str())); } #endif }; } // namespace bpftrace bpftrace-0.14.0/src/ast/bpforc/bpforcv1.cpp000066400000000000000000000055531413460502400204500ustar00rootroot00000000000000// Included by bpforc.cpp BpfOrc::BpfOrc(TargetMachine *TM, DataLayout DL) : TM(TM), DL(std::move(DL)), #if LLVM_VERSION_MAJOR >= 7 Resolver(createLegacyLookupResolver( ES, [](const std::string &Name __attribute__((unused))) -> JITSymbol { return nullptr; }, [](Error Err) { cantFail(std::move(Err), "lookup failed"); })), #endif #if LLVM_VERSION_MAJOR >= 9 ObjectLayer(AcknowledgeORCv1Deprecation, ES, [this](VModuleKey) { return LegacyRTDyldObjectLinkingLayer::Resources{ std::make_shared(sections_), Resolver }; }), CompileLayer(AcknowledgeORCv1Deprecation, ObjectLayer, SimpleCompiler(*this->TM)) { } #elif LLVM_VERSION_MAJOR == 8 ObjectLayer(ES, [this](VModuleKey) { return LegacyRTDyldObjectLinkingLayer::Resources{ std::make_shared(sections_), Resolver }; }), CompileLayer(ObjectLayer, SimpleCompiler(*this->TM)) { } #elif LLVM_VERSION_MAJOR == 7 ObjectLayer(ES, [this](VModuleKey) { return RTDyldObjectLinkingLayer::Resources{ std::make_shared(sections_), Resolver }; }), CompileLayer(ObjectLayer, SimpleCompiler(*this->TM)) { } #elif LLVM_VERSION_MAJOR <= 6 ObjectLayer( [this]() { return std::make_shared(sections_); }), CompileLayer(ObjectLayer, SimpleCompiler(*TM)) { } #endif #if LLVM_VERSION_MAJOR >= 7 void BpfOrc::compile(std::unique_ptr M) { auto K = ES.allocateVModule(); cantFail(CompileLayer.addModule(K, std::move(M))); cantFail(CompileLayer.emitAndFinalize(K)); } #else void BpfOrc::compile(std::unique_ptr M) { auto Resolver = createLambdaResolver( [](const std::string &) { return JITSymbol(nullptr); }, [](const std::string &) { return JITSymbol(nullptr); }); auto mod = cantFail( CompileLayer.addModule(std::move(M), std::move(Resolver))); cantFail(CompileLayer.emitAndFinalize(mod)); } #endif LLVMContext &BpfOrc::getContext() { return CTX; } std::unique_ptr BpfOrc::Create() { LLVMInitializeBPFTargetInfo(); LLVMInitializeBPFTarget(); LLVMInitializeBPFTargetMC(); LLVMInitializeBPFAsmPrinter(); std::string error; const Target *target = TargetRegistry::lookupTarget(LLVMTargetTriple, error); if (!target) throw std::runtime_error("Could not create LLVM target " + error); TargetOptions opt; auto RM = Reloc::Model(); auto TM = target->createTargetMachine( LLVMTargetTriple, "generic", "", opt, RM); return std::make_unique(TM, TM->createDataLayout()); } bpftrace-0.14.0/src/ast/bpforc/bpforcv2.cpp000066400000000000000000000027621413460502400204500ustar00rootroot00000000000000// Included by bpforc.cpp BpfOrc::BpfOrc(TargetMachine *TM, DataLayout DL, std::unique_ptr ES) : TM(std::move(TM)), DL(std::move(DL)), ES(std::move(ES)), ObjectLayer(*(this->ES), [this]() { return std::make_unique(sections_); }), CompileLayer(*this->ES, ObjectLayer, std::make_unique(*this->TM)), Mangle(*this->ES, this->DL), CTX(std::make_unique()), MainJD(cantFail(this->ES->createJITDylib("
"))) { } LLVMContext &BpfOrc::getContext() { return *CTX.getContext(); } std::unique_ptr BpfOrc::Create() { LLVMInitializeBPFTargetInfo(); LLVMInitializeBPFTarget(); LLVMInitializeBPFTargetMC(); LLVMInitializeBPFAsmPrinter(); auto JTMB = cantFail( Expected(Triple(LLVMTargetTriple))); // return unique_ptrs auto DL = cantFail(JTMB.getDefaultDataLayoutForTarget()); auto TM = cantFail(JTMB.createTargetMachine()); #if LLVM_VERSION_MAJOR >= 13 auto EPC = SelfExecutorProcessControl::Create(); auto ES = std::make_unique(std::move(*EPC)); #else auto ES = std::make_unique(); #endif return std::make_unique(TM.release(), std::move(DL), std::move(ES)); } void BpfOrc::compile(std::unique_ptr M) { cantFail(CompileLayer.add(MainJD, ThreadSafeModule(std::move(M), CTX))); } bpftrace-0.14.0/src/ast/codegen_helper.h000066400000000000000000000013001413460502400200450ustar00rootroot00000000000000#pragma once #include "bpftrace.h" namespace bpftrace { namespace ast { inline bool needMemcpy(const SizedType &stype) { return stype.IsAggregate() || stype.IsTimestampTy(); } inline bool shouldBeOnStackAlready(const SizedType &type) { return type.IsStringTy() || type.IsBufferTy() || type.IsInetTy() || type.IsUsymTy() || type.IsTupleTy() || type.IsTimestampTy() || type.IsMacAddressTy(); } inline bool onStack(const SizedType &type) { return type.is_internal || shouldBeOnStackAlready(type); } inline AddrSpace find_addrspace_stack(const SizedType &ty) { return (shouldBeOnStackAlready(ty)) ? AddrSpace::kernel : ty.GetAS(); } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/int_parser.cpp000066400000000000000000000072151413460502400176160ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "int_parser.h" namespace { template T _parse_int(const std::string &num __attribute__((unused)), size_t *idx __attribute__((unused)), int base __attribute__((unused))) { static_assert(not std::is_same_v, "BUG: _parse_int not implemented for type"); } template <> int64_t _parse_int(const std::string &num, size_t *idx, int base) { return std::stoll(num, idx, base); } template <> uint64_t _parse_int(const std::string &num, size_t *idx, int base) { return std::stoull(num, idx, base); } template std::variant _parse_int(const std::string &num, int base) { // https://en.cppreference.com/w/cpp/language/integer_literal#The_type_of_the_literal static auto int_size_re = std::regex("^(u|u?l?l)$", std::regex::icase); try { std::size_t idx; T ret = _parse_int(num, &idx, base); if (idx != num.size()) { auto trail = num.substr(idx, std::string::npos); auto match = std::regex_match(trail, int_size_re); if (!match) return "Found trailing non-numeric characters"; } return ret; } catch (const std::exception &ex) { return ex.what(); } } // integer variant of 10^exp uint64_t _ten_pow(uint64_t exp) { static const uint64_t v[] = { 1, 10, 100, 1000, 10000, 100000, 1000000 }; if (exp > 6) return v[6] * _ten_pow(exp - 6); return v[exp]; } // integer variant of scientific notation parsing template std::variant _parse_exp(const std::string &coeff, const std::string &exp) { std::stringstream errmsg; auto maybe_coeff = _parse_int(coeff, 10); if (std::string *err = std::get_if(&maybe_coeff)) { errmsg << "Coefficient part of scientific literal is not a valid number: " << coeff << ": " << *err; return errmsg.str(); } auto maybe_exp = _parse_int(exp, 10); if (std::string *err = std::get_if(&maybe_exp)) { errmsg << "Exponent part of scientific literal is not a valid number: " << exp << ": " << *err; return errmsg.str(); } auto c = std::get(maybe_coeff); auto e = std::get(maybe_exp); if (c > 9) { errmsg << "Coefficient part of scientific literal must be in range (0,9), " "got: " << coeff; return errmsg.str(); } if (e > 16) { errmsg << "Exponent will overflow integer range: " << exp; return errmsg.str(); } return c * (T)_ten_pow(e); } } // namespace namespace bpftrace { namespace ast { namespace int_parser { template T to_int(const std::string &num, int base) { std::string n(num); n.erase(std::remove(n.begin(), n.end(), '_'), n.end()); std::variant res; // If hex if ((n.rfind("0x", 0) == 0) || (n.rfind("0X", 0) == 0)) { res = _parse_int(n, base); } else { auto pos = n.find_first_of("eE"); if (pos != std::string::npos) { res = _parse_exp(n.substr(0, pos), n.substr(pos + 1, std::string::npos)); } else { res = _parse_int(n, base); } } if (std::string *err = std::get_if(&res)) throw std::invalid_argument(*err); return std::get(res); } int64_t to_int(const std::string &num, int base) { return to_int(num, base); } uint64_t to_uint(const std::string &num, int base) { return to_int(num, base); } } // namespace int_parser } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/int_parser.h000066400000000000000000000011061413460502400172540ustar00rootroot00000000000000#include namespace bpftrace { namespace ast { namespace int_parser { /* String -> int conversion specific to bpftrace - error when trailing characters are found - supports scientific notation, e.g. 1e6 - error when out of int range (1e20) - error when base > 9 (12e3) - support underscore as separator, e.g. 1_234_000 All errors are raised as std::invalid_argument exception */ int64_t to_int(const std::string &num, int base); uint64_t to_uint(const std::string &num, int base); } // namespace int_parser } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/irbuilderbpf.cpp000066400000000000000000001277311413460502400201270ustar00rootroot00000000000000#include #include #include "arch/arch.h" #include "ast/async_event_types.h" #include "bpftrace.h" #include "codegen_helper.h" #include "irbuilderbpf.h" #include "log.h" #include "utils.h" #include #include namespace libbpf { #undef __BPF_FUNC_MAPPER #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace { namespace 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: throw std::runtime_error("BUG: unknown probe_read id: " + std::to_string(id)); } } } // namespace libbpf::bpf_func_id IRBuilderBPF::selectProbeReadHelper(AddrSpace as, bool str) { libbpf::bpf_func_id fn; // Assume that if a kernel has probe_read_kernel it has the other 3 too if (bpftrace_.feature_->has_helper_probe_read_kernel()) { if (as == AddrSpace::kernel) { fn = str ? libbpf::BPF_FUNC_probe_read_kernel_str : libbpf::BPF_FUNC_probe_read_kernel; } else if (as == AddrSpace::user) { fn = str ? libbpf::BPF_FUNC_probe_read_user_str : libbpf::BPF_FUNC_probe_read_user; } else { // if the kernel has the new helpers but AS is still none it is a bug // in bpftrace, assert catches it for debug builds. // assert(as != AddrSpace::none); static bool warnonce = false; if (!warnonce) { warnonce = true; LOG(WARNING) << "Addrspace is not set"; } fn = str ? libbpf::BPF_FUNC_probe_read_str : libbpf::BPF_FUNC_probe_read; } } else { fn = str ? libbpf::BPF_FUNC_probe_read_str : libbpf::BPF_FUNC_probe_read; } return fn; } AllocaInst *IRBuilderBPF::CreateUSym(llvm::Value *val) { std::vector elements = { getInt64Ty(), // addr getInt64Ty(), // pid }; StructType *usym_t = GetStructType("usym_t", elements, false); AllocaInst *buf = CreateAllocaBPF(usym_t, "usym"); Value *pid = CreateLShr(CreateGetPidTgid(), 32); // The extra 0 here ensures the type of addr_offset will be int64 Value *addr_offset = CreateGEP(buf, { getInt64(0), getInt32(0) }); Value *pid_offset = CreateGEP(buf, { getInt64(0), getInt32(1) }); CreateStore(val, addr_offset); CreateStore(pid, pid_offset); return buf; } StructType *IRBuilderBPF::GetStructType( std::string name, const std::vector &elements, bool packed) { auto search = structs_.find(name); if (search != structs_.end()) return search->second; StructType *s = StructType::create(elements, name, packed); structs_.insert({ name, s }); return s; } IRBuilderBPF::IRBuilderBPF(LLVMContext &context, Module &module, BPFtrace &bpftrace) : IRBuilder<>(context), module_(module), bpftrace_(bpftrace) { // Declare external LLVM function FunctionType *pseudo_func_type = FunctionType::get( getInt64Ty(), {getInt64Ty(), getInt64Ty()}, false); Function::Create( pseudo_func_type, GlobalValue::ExternalLinkage, "llvm.bpf.pseudo", &module_); } void IRBuilderBPF::hoist(const std::function &functor) { 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, llvm::Value *arraysize, const std::string &name) { AllocaInst *alloca; hoist([this, ty, arraysize, &name, &alloca]() { alloca = CreateAlloca(ty, arraysize, name); }); CreateLifetimeStart(alloca); return alloca; } AllocaInst *IRBuilderBPF::CreateAllocaBPF(llvm::Type *ty, const std::string &name) { return CreateAllocaBPF(ty, nullptr, name); } AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, const std::string &name) { llvm::Type *ty = GetType(stype); return CreateAllocaBPF(ty, nullptr, name); } AllocaInst *IRBuilderBPF::CreateAllocaBPFInit(const SizedType &stype, const std::string &name) { AllocaInst *alloca; hoist([this, &stype, &name, &alloca]() { llvm::Type *ty = GetType(stype); alloca = CreateAlloca(ty, nullptr, name); CreateLifetimeStart(alloca); if (needMemcpy(stype)) { CREATE_MEMSET(alloca, getInt8(0), stype.GetSize(), 1); } else { CreateStore(ConstantInt::get(ty, 0), alloca); } }); return alloca; } AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, llvm::Value *arraysize, const std::string &name) { llvm::Type *ty = GetType(stype); return CreateAllocaBPF(ty, arraysize, name); } AllocaInst *IRBuilderBPF::CreateAllocaBPF(int bytes, const std::string &name) { llvm::Type *ty = ArrayType::get(getInt8Ty(), bytes); return CreateAllocaBPF(ty, name); } 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); } 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::ostringstream ty_name; for (const auto &elem : stype.GetFields()) { auto &elemtype = elem.type; llvm_elems.emplace_back(GetType(elemtype)); ty_name << elemtype << "_"; } ty_name << "_tuple_t"; ty = GetStructType(ty_name.str(), llvm_elems, false); } else if (stype.IsPtrTy()) { ty = getInt64Ty(); } 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(FATAL) << stype.GetSize() << " is not a valid type size for GetType"; } } return ty; } CallInst *IRBuilderBPF::createCall(Value *callee, ArrayRef args, const Twine &Name) { #if LLVM_VERSION_MAJOR >= 11 auto *calleePtrType = cast(callee->getType()); auto *calleeType = cast(calleePtrType->getElementType()); return CreateCall(calleeType, callee, args, Name); #else return CreateCall(callee, args, Name); #endif } CallInst *IRBuilderBPF::CreateBpfPseudoCallId(int mapid) { Function *pseudo_func = module_.getFunction("llvm.bpf.pseudo"); return createCall(pseudo_func, { getInt64(BPF_PSEUDO_MAP_FD), getInt64(mapid) }, "pseudo"); } CallInst *IRBuilderBPF::CreateBpfPseudoCallId(Map &map) { int mapid = bpftrace_.maps[map.ident].value()->id; return CreateBpfPseudoCallId(mapid); } CallInst *IRBuilderBPF::CreateBpfPseudoCallValue(int mapid) { Function *pseudo_func = module_.getFunction("llvm.bpf.pseudo"); return CreateCall(pseudo_func, { getInt64(BPF_PSEUDO_MAP_VALUE), getInt64(mapid) }, "pseudo"); } CallInst *IRBuilderBPF::CreateBpfPseudoCallValue(Map &map) { int mapid = bpftrace_.maps[map.ident].value()->id; return CreateBpfPseudoCallValue(mapid); } CallInst *IRBuilderBPF::createMapLookup(int mapid, Value *key) { Value *map_ptr = CreateBpfPseudoCallId(mapid); // 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( getInt8PtrTy(), { map_ptr->getType(), key->getType() }, false); PointerType *lookup_func_ptr_type = PointerType::get(lookup_func_type, 0); Constant *lookup_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_map_lookup_elem), lookup_func_ptr_type); return createCall(lookup_func, { map_ptr, key }, "lookup_elem"); } CallInst *IRBuilderBPF::CreateGetJoinMap(Value *ctx, const location &loc) { AllocaInst *key = CreateAllocaBPF(getInt32Ty(), "key"); CreateStore(getInt32(0), key); CallInst *call = createMapLookup( bpftrace_.maps[MapManager::Type::Join].value()->id, key); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_map_lookup_elem, loc, true); return call; } Value *IRBuilderBPF::CreateMapLookupElem(Value *ctx, Map &map, Value *key, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); int mapid = bpftrace_.maps[map.ident].value()->id; return CreateMapLookupElem(ctx, mapid, key, map.type, loc); } Value *IRBuilderBPF::CreateMapLookupElem(Value *ctx, int mapid, Value *key, SizedType &type, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); CallInst *call = createMapLookup(mapid, key); // Check if result == 0 Function *parent = GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_.getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_.getContext(), "lookup_failure", parent); BasicBlock *lookup_merge_block = BasicBlock::Create(module_.getContext(), "lookup_merge", parent); AllocaInst *value = CreateAllocaBPF(type, "lookup_elem_val"); Value *condition = CreateICmpNE( CreateIntCast(call, getInt8PtrTy(), true), ConstantExpr::getCast(Instruction::IntToPtr, getInt64(0), getInt8PtrTy()), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); if (needMemcpy(type)) CREATE_MEMCPY(value, call, type.GetSize(), 1); else { assert(value->getType()->isPointerTy() && (value->getType()->getElementType() == getInt64Ty())); // createMapLookup returns an u8* auto *cast = CreatePointerCast(call, value->getType(), "cast"); CreateStore(CreateLoad(getInt64Ty(), cast), value); } CreateBr(lookup_merge_block); SetInsertPoint(lookup_failure_block); if (needMemcpy(type)) CREATE_MEMSET(value, getInt8(0), type.GetSize(), 1); else CreateStore(getInt64(0), value); CreateHelperError(ctx, getInt32(0), libbpf::BPF_FUNC_map_lookup_elem, loc); CreateBr(lookup_merge_block); SetInsertPoint(lookup_merge_block); if (needMemcpy(type)) return value; Value *ret = CreateLoad(value); CreateLifetimeEnd(value); return ret; } void IRBuilderBPF::CreateMapUpdateElem(Value *ctx, Map &map, Value *key, Value *val, const location &loc) { Value *map_ptr = CreateBpfPseudoCallId(map); assert(ctx && ctx->getType() == getInt8PtrTy()); assert(key->getType()->isPointerTy()); assert(val->getType()->isPointerTy()); Value *flags = getInt64(0); // int map_update_elem(struct bpf_map * map, void *key, void * value, u64 // flags) Return: 0 on success or negative error FunctionType *update_func_type = FunctionType::get( getInt64Ty(), { map_ptr->getType(), key->getType(), val->getType(), getInt64Ty() }, false); PointerType *update_func_ptr_type = PointerType::get(update_func_type, 0); Constant *update_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_map_update_elem), update_func_ptr_type); CallInst *call = createCall(update_func, { map_ptr, key, val, flags }, "update_elem"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_map_update_elem, loc); } void IRBuilderBPF::CreateMapDeleteElem(Value *ctx, Map &map, Value *key, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); assert(key->getType()->isPointerTy()); Value *map_ptr = CreateBpfPseudoCallId(map); // int map_delete_elem(&map, &key) // Return: 0 on success or negative error FunctionType *delete_func_type = FunctionType::get( getInt64Ty(), { map_ptr->getType(), key->getType() }, false); PointerType *delete_func_ptr_type = PointerType::get(delete_func_type, 0); Constant *delete_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_map_delete_elem), delete_func_ptr_type); CallInst *call = createCall(delete_func, { map_ptr, key }, "delete_elem"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_map_delete_elem, loc); } void IRBuilderBPF::CreateProbeRead(Value *ctx, Value *dst, size_t size, Value *src, AddrSpace as, const location &loc) { return CreateProbeRead(ctx, dst, getInt32(size), src, as, loc); } void IRBuilderBPF::CreateProbeRead(Value *ctx, Value *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); assert(size && size->getType()->getIntegerBitWidth() <= 32); size = CreateIntCast(size, getInt32Ty(), false); // int bpf_probe_read(void *dst, int size, void *src) // Return: 0 on success or negative error auto read_fn = selectProbeReadHelper(as, false); FunctionType *proberead_func_type = FunctionType::get( getInt64Ty(), { dst->getType(), getInt32Ty(), src->getType() }, false); PointerType *proberead_func_ptr_type = PointerType::get(proberead_func_type, 0); Constant *proberead_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(read_fn), proberead_func_ptr_type); CallInst *call = createCall(proberead_func, { dst, size, src }, probeReadHelperName(read_fn)); CreateHelperErrorCond(ctx, call, read_fn, loc); } Constant *IRBuilderBPF::createProbeReadStrFn(llvm::Type *dst, llvm::Type *src, AddrSpace as) { assert(src && (src->isIntegerTy() || src->isPointerTy())); // int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr) FunctionType *probereadstr_func_type = FunctionType::get( getInt64Ty(), { dst, getInt32Ty(), src }, false); PointerType *probereadstr_func_ptr_type = PointerType::get( probereadstr_func_type, 0); return ConstantExpr::getCast(Instruction::IntToPtr, getInt64(selectProbeReadHelper(as, true)), probereadstr_func_ptr_type); } CallInst *IRBuilderBPF::CreateProbeReadStr(Value *ctx, AllocaInst *dst, size_t size, Value *src, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); return CreateProbeReadStr(ctx, dst, getInt32(size), src, as, loc); } CallInst *IRBuilderBPF::CreateProbeReadStr(Value *ctx, Value *dst, size_t size, Value *src, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); Constant *fn = createProbeReadStrFn(dst->getType(), src->getType(), as); auto read_fn = selectProbeReadHelper(as, true); CallInst *call = createCall(fn, { dst, getInt32(size), src }, probeReadHelperName(read_fn)); CreateHelperErrorCond(ctx, call, read_fn, loc); return call; } CallInst *IRBuilderBPF::CreateProbeReadStr(Value *ctx, AllocaInst *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); assert(dst && dst->getAllocatedType()->isArrayTy() && dst->getAllocatedType()->getArrayElementType() == getInt8Ty()); assert(size && size->getType()->isIntegerTy()); auto *size_i32 = CreateIntCast(size, getInt32Ty(), false); Constant *fn = createProbeReadStrFn(dst->getType(), src->getType(), as); auto read_fn = selectProbeReadHelper(as, true); CallInst *call = createCall(fn, { dst, size_i32, src }, probeReadHelperName(read_fn)); CreateHelperErrorCond(ctx, call, read_fn, loc); return call; } Value *IRBuilderBPF::CreateUSDTReadArgument(Value *ctx, struct bcc_usdt_argument *argument, Builtin &builtin, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); // Argument size must be 1, 2, 4, or 8. See // https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation int abs_size = std::abs(argument->size); assert(abs_size == 1 || abs_size == 2 || abs_size == 4 || abs_size == 8); if (argument->valid & BCC_USDT_ARGUMENT_DEREF_IDENT) LOG(ERROR) << "deref ident is not handled yet [" << argument->deref_ident << "]"; // USDT arguments can be any valid gas (GNU asm) operand. // BCC normalises these into the bcc_usdt_argument and supports most // valid gas operands. // // This code handles the following argument types: // * A constant (ARGUMENT_CONSTANT) // // * The value of a register (ARGUMENT_BASE_REGISTER_NAME without // ARGUMENT_DEREF_OFFSET set). // // * The value at address: base_register + offset + (index_register * scale) // Where index_register and scale are optional. // Note: Offset is optional in the gas operand, however will be set as zero // if the register needs to be dereferenced. if (argument->valid & BCC_USDT_ARGUMENT_CONSTANT) { // Correctly sign extend and convert to a 64-bit int return CreateIntCast(getIntN(abs_size * 8, argument->constant), getInt64Ty(), argument->size < 0); } if (argument->valid & BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME && !(argument->valid & BCC_USDT_ARGUMENT_BASE_REGISTER_NAME)) { // Invalid combination?? LOG(ERROR) << "index register set without base register;" << " this case is not yet handled"; } Value *result = nullptr; if (argument->valid & BCC_USDT_ARGUMENT_BASE_REGISTER_NAME) { int offset = 0; offset = arch::offset(argument->base_register_name); if (offset < 0) { LOG(FATAL) << "offset for register " << argument->base_register_name << " not known"; } // bpftrace's args are internally represented as 64 bit integers. However, // the underlying argument (of the target program) may be less than 64 // bits. So we must be careful to zero out unused bits. Value* reg = CreateGEP(ctx, getInt64(offset * sizeof(uintptr_t)), "load_register"); AllocaInst *dst = CreateAllocaBPF(builtin.type, builtin.ident); Value *index_offset = nullptr; if (argument->valid & BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME) { int ioffset = arch::offset(argument->index_register_name); if (ioffset < 0) { LOG(FATAL) << "offset for register " << argument->index_register_name << " not known"; } index_offset = CreateGEP(ctx, getInt64(ioffset * sizeof(uintptr_t)), "load_register"); index_offset = CreateLoad(getInt64Ty(), index_offset); if (argument->valid & BCC_USDT_ARGUMENT_SCALE) { index_offset = CreateMul(index_offset, getInt64(argument->scale)); } } if (argument->valid & BCC_USDT_ARGUMENT_DEREF_OFFSET) { Value *ptr = CreateAdd(CreateLoad(getInt64Ty(), reg), getInt64(argument->deref_offset)); if (index_offset) { ptr = CreateAdd(ptr, index_offset); } CreateProbeRead(ctx, dst, abs_size, ptr, as, loc); result = CreateLoad(getIntNTy(abs_size * 8), dst); } else { result = CreateLoad(getIntNTy(abs_size * 8), reg); } // Sign extend and convert to a bpftools standard 64-bit integer type result = CreateIntCast(result, getInt64Ty(), argument->size < 0); CreateLifetimeEnd(dst); } return result; } Value *IRBuilderBPF::CreateUSDTReadArgument(Value *ctx, AttachPoint *attach_point, int usdt_location_index, int arg_num, Builtin &builtin, pid_t pid, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); struct bcc_usdt_argument argument; void *usdt; if (pid) { //FIXME use attach_point->target when iovisor/bcc#2064 is merged usdt = bcc_usdt_new_frompid(pid, nullptr); } else { usdt = bcc_usdt_new_frompath(attach_point->target.c_str()); } if (usdt == nullptr) { LOG(ERROR) << "failed to initialize usdt context for probe " << attach_point->target; exit(-1); } std::string ns = attach_point->usdt.provider; std::string func = attach_point->usdt.name; if (bcc_usdt_get_argument(usdt, ns.c_str(), func.c_str(), usdt_location_index, arg_num, &argument) != 0) { LOG(ERROR) << "couldn't get argument " << arg_num << " for " << attach_point->target << ":" << attach_point->ns << ":" << attach_point->func; exit(-2); } Value *result = CreateUSDTReadArgument(ctx, &argument, builtin, as, loc); bcc_usdt_close(usdt); return result; } Value *IRBuilderBPF::CreateStrncmp(Value *val1, Value *val2, uint64_t n, bool inverse) { /* // This function compares each character of the two string. // It returns true if all are equal and false if any are different // strcmp(String val1, String val2) { for (size_t i = 0; i < n; i++) { if (val1[i] != val2[i]) { return false; } if (val1[i] == NULL) { return true; } } return true; } */ // Check if the compared strings are literals. // If so, we can avoid storing the literal in memory. std::optional literal1; if (auto constString1 = dyn_cast(val1)) literal1 = constString1->getAsString(); else if (isa(val1)) literal1 = ""; else literal1 = std::nullopt; std::optional literal2; if (auto constString2 = dyn_cast(val2)) literal2 = constString2->getAsString(); else if (isa(val2)) literal2 = ""; else literal2 = std::nullopt; #ifndef NDEBUG if (!literal1) { PointerType *val1p = cast(val1->getType()); assert(val1p->getElementType()->isArrayTy() && val1p->getElementType()->getArrayElementType() == getInt8Ty()); } if (!literal2) { PointerType *val2p = cast(val2->getType()); assert(val2p->getElementType()->isArrayTy() && val2p->getElementType()->getArrayElementType() == getInt8Ty()); } #endif Function *parent = GetInsertBlock()->getParent(); AllocaInst *store = CreateAllocaBPF(getInt1Ty(), "strcmp.result"); BasicBlock *str_ne = BasicBlock::Create(module_.getContext(), "strcmp.false", parent); BasicBlock *done = BasicBlock::Create(module_.getContext(), "strcmp.done", parent); CreateStore(getInt1(!inverse), store); Value *null_byte = getInt8(0); for (size_t i = 0; i < n; i++) { BasicBlock *char_eq = BasicBlock::Create(module_.getContext(), "strcmp.loop", parent); BasicBlock *loop_null_check = BasicBlock::Create(module_.getContext(), "strcmp.loop_null_cmp", parent); Value *l; if (literal1) l = getInt8(literal1->c_str()[i]); else { auto *ptr_l = CreateGEP(val1, { getInt32(0), getInt32(i) }); l = CreateLoad(getInt8Ty(), ptr_l); } Value *r; if (literal2) r = getInt8(literal2->c_str()[i]); else { auto *ptr_r = CreateGEP(val2, { getInt32(0), getInt32(i) }); r = CreateLoad(getInt8Ty(), ptr_r); } Value *cmp = CreateICmpNE(l, r, "strcmp.cmp"); CreateCondBr(cmp, str_ne, loop_null_check); SetInsertPoint(loop_null_check); Value *cmp_null = CreateICmpEQ(l, null_byte, "strcmp.cmp_null"); CreateCondBr(cmp_null, done, char_eq); SetInsertPoint(char_eq); } CreateBr(done); SetInsertPoint(done); CreateStore(getInt1(inverse), store); CreateBr(str_ne); SetInsertPoint(str_ne); Value *result = CreateLoad(store); CreateLifetimeEnd(store); result = CreateIntCast(result, getInt64Ty(), false); return result; } CallInst *IRBuilderBPF::CreateGetNs(bool boot_time) { // u64 ktime_get_ns() // Return: current ktime auto fn = boot_time ? libbpf::BPF_FUNC_ktime_get_boot_ns : libbpf::BPF_FUNC_ktime_get_ns; FunctionType *gettime_func_type = FunctionType::get(getInt64Ty(), false); PointerType *gettime_func_ptr_type = PointerType::get(gettime_func_type, 0); Constant *gettime_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(fn), gettime_func_ptr_type); return createCall(gettime_func, {}, "get_ns"); } CallInst *IRBuilderBPF::CreateGetPidTgid() { // u64 bpf_get_current_pid_tgid(void) // Return: current->tgid << 32 | current->pid FunctionType *getpidtgid_func_type = FunctionType::get(getInt64Ty(), false); PointerType *getpidtgid_func_ptr_type = PointerType::get(getpidtgid_func_type, 0); Constant *getpidtgid_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_get_current_pid_tgid), getpidtgid_func_ptr_type); return createCall(getpidtgid_func, {}, "get_pid_tgid"); } CallInst *IRBuilderBPF::CreateGetCurrentCgroupId() { // u64 bpf_get_current_cgroup_id(void) // Return: 64-bit cgroup-v2 id FunctionType *getcgroupid_func_type = FunctionType::get(getInt64Ty(), false); PointerType *getcgroupid_func_ptr_type = PointerType::get( getcgroupid_func_type, 0); Constant *getcgroupid_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_get_current_cgroup_id), getcgroupid_func_ptr_type); return createCall(getcgroupid_func, {}, "get_cgroup_id"); } CallInst *IRBuilderBPF::CreateGetUidGid() { // u64 bpf_get_current_uid_gid(void) // Return: current_gid << 32 | current_uid FunctionType *getuidgid_func_type = FunctionType::get(getInt64Ty(), false); PointerType *getuidgid_func_ptr_type = PointerType::get(getuidgid_func_type, 0); Constant *getuidgid_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_get_current_uid_gid), getuidgid_func_ptr_type); return createCall(getuidgid_func, {}, "get_uid_gid"); } CallInst *IRBuilderBPF::CreateGetCpuId() { // u32 bpf_raw_smp_processor_id(void) // Return: SMP processor ID FunctionType *getcpuid_func_type = FunctionType::get(getInt64Ty(), false); PointerType *getcpuid_func_ptr_type = PointerType::get(getcpuid_func_type, 0); Constant *getcpuid_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_get_smp_processor_id), getcpuid_func_ptr_type); 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(libbpf::BPF_FUNC_get_current_task), getcurtask_func_ptr_type); return createCall(getcurtask_func, {}, "get_cur_task"); } CallInst *IRBuilderBPF::CreateGetRandom() { // u64 bpf_get_prandom_u32(void) // Return: random FunctionType *getrandom_func_type = FunctionType::get(getInt64Ty(), false); PointerType *getrandom_func_ptr_type = PointerType::get(getrandom_func_type, 0); Constant *getrandom_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_get_prandom_u32), getrandom_func_ptr_type); return createCall(getrandom_func, {}, "get_random"); } CallInst *IRBuilderBPF::CreateGetStackId(Value *ctx, bool ustack, StackType stack_type, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); Value *map_ptr = CreateBpfPseudoCallId( bpftrace_.maps[stack_type].value()->id); int flags = 0; if (ustack) flags |= (1<<8); Value *flags_val = getInt64(flags); // int bpf_get_stackid(struct pt_regs *ctx, struct bpf_map *map, u64 flags) // Return: >= 0 stackid on success or negative error FunctionType *getstackid_func_type = FunctionType::get( getInt64Ty(), { getInt8PtrTy(), map_ptr->getType(), getInt64Ty() }, false); PointerType *getstackid_func_ptr_type = PointerType::get(getstackid_func_type, 0); Constant *getstackid_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_get_stackid), getstackid_func_ptr_type); CallInst *call = createCall(getstackid_func, { ctx, map_ptr, flags_val }, "get_stackid"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_get_stackid, loc); return call; } void IRBuilderBPF::CreateGetCurrentComm(Value *ctx, AllocaInst *buf, size_t size, const location &loc) { assert(buf->getType()->getElementType()->isArrayTy() && buf->getType()->getElementType()->getArrayNumElements() >= size && buf->getType()->getElementType()->getArrayElementType() == getInt8Ty()); // int 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); PointerType *getcomm_func_ptr_type = PointerType::get(getcomm_func_type, 0); Constant *getcomm_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_get_current_comm), getcomm_func_ptr_type); CallInst *call = createCall(getcomm_func, { buf, getInt64(size) }, "get_comm"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_get_current_comm, loc); } void IRBuilderBPF::CreatePerfEventOutput(Value *ctx, Value *data, size_t size) { assert(ctx && ctx->getType() == getInt8PtrTy()); assert(data && data->getType()->isPointerTy()); Value *map_ptr = CreateBpfPseudoCallId( bpftrace_.maps[MapManager::Type::PerfEvent].value()->id); Value *flags_val = getInt64(BPF_F_CURRENT_CPU); Value *size_val = getInt64(size); // int bpf_perf_event_output(struct pt_regs *ctx, struct bpf_map *map, // u64 flags, void *data, u64 size) FunctionType *perfoutput_func_type = FunctionType::get(getInt64Ty(), { getInt8PtrTy(), map_ptr->getType(), getInt64Ty(), data->getType(), getInt64Ty() }, false); PointerType *perfoutput_func_ptr_type = PointerType::get(perfoutput_func_type, 0); Constant *perfoutput_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_perf_event_output), perfoutput_func_ptr_type); createCall(perfoutput_func, { ctx, map_ptr, flags_val, data, size_val }, "perf_event_output"); } void IRBuilderBPF::CreateSignal(Value *ctx, Value *sig, const location &loc) { // int bpf_send_signal(u32 sig) // Return: 0 or error FunctionType *signal_func_type = FunctionType::get( getInt64Ty(), {getInt32Ty()}, false); PointerType *signal_func_ptr_type = PointerType::get(signal_func_type, 0); Constant *signal_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_send_signal), signal_func_ptr_type); CallInst *call = createCall(signal_func, { sig }, "signal"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_send_signal, loc); } void IRBuilderBPF::CreateOverrideReturn(Value *ctx, Value *rc) { // int bpf_override_return(struct pt_regs *regs, u64 rc) // Return: 0 FunctionType *override_func_type = FunctionType::get( getInt64Ty(), { getInt8PtrTy(), getInt64Ty() }, false); PointerType *override_func_ptr_type = PointerType::get(override_func_type, 0); Constant *override_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_override_return), override_func_ptr_type); createCall(override_func, { ctx, rc }, "override"); } Value *IRBuilderBPF::CreatKFuncArg(Value *ctx, SizedType &type, std::string &name) { assert(type.IsIntTy() || type.IsPtrTy()); ctx = CreatePointerCast(ctx, getInt64Ty()->getPointerTo()); Value *expr = CreateLoad(GetType(type), CreateGEP(ctx, getInt64(type.funcarg_idx)), name); // LLVM 7.0 <= does not have CreateLoad(*Ty, *Ptr, isVolatile, Name), // so call setVolatile() manually dyn_cast(expr)->setVolatile(true); return expr; } Value *IRBuilderBPF::CreateRegisterRead(Value *ctx, const std::string &builtin) { int offset; if (builtin == "retval") offset = arch::ret_offset(); else if (builtin == "func") offset = arch::pc_offset(); else // argX offset = arch::arg_offset(atoi(builtin.substr(3).c_str())); Value *ctx_ptr = CreatePointerCast(ctx, getInt64Ty()->getPointerTo()); // LLVM optimization is possible to transform `(uint64*)ctx` into // `(uint8*)ctx`, but sometimes this causes invalid context access. // Mark every context access to suppress any LLVM optimization. Value *result = CreateLoad(getInt64Ty(), CreateGEP(ctx_ptr, getInt64(offset)), builtin); // LLVM 7.0 <= does not have CreateLoad(*Ty, *Ptr, isVolatile, Name), // so call setVolatile() manually dyn_cast(result)->setVolatile(true); return result; } static bool return_zero_if_err(libbpf::bpf_func_id func_id) { switch (func_id) { /* * When these function fails, bpftrace stores zero as a result. * A user script can check an error by seeing the value. * Therefore error checks of these functions are omitted if * helper_check_level == 1. */ case libbpf::BPF_FUNC_probe_read: case libbpf::BPF_FUNC_probe_read_str: case libbpf::BPF_FUNC_probe_read_kernel: case libbpf::BPF_FUNC_probe_read_kernel_str: case libbpf::BPF_FUNC_probe_read_user: case libbpf::BPF_FUNC_probe_read_user_str: case libbpf::BPF_FUNC_map_lookup_elem: return true; default: return false; } return false; } void IRBuilderBPF::CreateHelperError(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location &loc) { assert(ctx && ctx->getType() == getInt8PtrTy()); 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 = helper_error_id_++; bpftrace_.resources.helper_error_info[error_id] = { .func_id = func_id, .loc = loc }; auto elements = AsyncEvent::HelperError().asLLVMType(*this); StructType *helper_error_struct = GetStructType("helper_error_t", elements, true); AllocaInst *buf = CreateAllocaBPF(helper_error_struct, "helper_error_t"); CreateStore(GetIntSameSize(asyncactionint(AsyncAction::helper_error), elements.at(0)), CreateGEP(buf, { getInt64(0), getInt32(0) })); CreateStore(GetIntSameSize(error_id, elements.at(1)), CreateGEP(buf, { getInt64(0), getInt32(1) })); CreateStore(return_value, CreateGEP(buf, { getInt64(0), getInt32(2) })); auto &layout = module_.getDataLayout(); auto struct_size = layout.getTypeAllocSize(helper_error_struct); CreatePerfEventOutput(ctx, buf, struct_size); CreateLifetimeEnd(buf); } // Report error if a return value < 0 (or return value == 0 if compare_zero is // true) void IRBuilderBPF::CreateHelperErrorCond(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location &loc, bool compare_zero) { assert(ctx && ctx->getType() == getInt8PtrTy()); if (bpftrace_.helper_check_level_ == 0 || (bpftrace_.helper_check_level_ == 1 && return_zero_if_err(func_id))) return; Function *parent = GetInsertBlock()->getParent(); BasicBlock *helper_failure_block = BasicBlock::Create(module_.getContext(), "helper_failure", parent); BasicBlock *helper_merge_block = BasicBlock::Create(module_.getContext(), "helper_merge", parent); // A return type of most helper functions are Int64Ty but they actually // return int and we need to use Int32Ty value to check if a return // value is negative. TODO: use Int32Ty as a return type auto *ret = CreateIntCast(return_value, getInt32Ty(), true); Value *condition; if (compare_zero) condition = CreateICmpNE(ret, Constant::getNullValue(ret->getType())); else condition = CreateICmpSGE(ret, Constant::getNullValue(ret->getType())); CreateCondBr(condition, helper_merge_block, helper_failure_block); SetInsertPoint(helper_failure_block); CreateHelperError(ctx, ret, func_id, loc); CreateBr(helper_merge_block); SetInsertPoint(helper_merge_block); } void IRBuilderBPF::CreatePath(Value *ctx, AllocaInst *buf, Value *path, 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(), { getInt8PtrTy(), buf->getType(), getInt32Ty() }, false); PointerType *d_path_func_ptr_type = PointerType::get(d_path_func_type, 0); Constant *d_path_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64( libbpf::BPF_FUNC_d_path), d_path_func_ptr_type); CallInst *call = createCall(d_path_func, { path, buf, getInt32(bpftrace_.strlen_) }, "d_path"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_d_path, loc); } void IRBuilderBPF::CreateSeqPrintf(Value *ctx, Value *fmt, Value *fmt_size, AllocaInst *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(), getInt8PtrTy(), getInt32Ty(), getInt8PtrTy(), getInt32Ty() }, false); PointerType *seq_printf_func_ptr_type = PointerType::get(seq_printf_func_type, 0); Constant *seq_printf_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_seq_printf), seq_printf_func_ptr_type); ctx = CreatePointerCast(ctx, getInt8Ty()->getPointerTo()); Value *meta = CreateLoad(getInt64Ty()->getPointerTo(), CreateGEP(ctx, getInt64(0)), "meta"); dyn_cast(meta)->setVolatile(true); Value *seq = CreateLoad(getInt64Ty(), CreateGEP(meta, getInt64(0)), "seq"); CallInst *call = createCall(seq_printf_func, { seq, fmt, fmt_size, data, data_len }, "seq_printf"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_seq_printf, loc); } StoreInst *IRBuilderBPF::createAlignedStore(Value *val, Value *ptr, unsigned int align) { #if LLVM_VERSION_MAJOR < 10 return CreateAlignedStore(val, ptr, align); #else return CreateAlignedStore(val, ptr, MaybeAlign(align)); #endif } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/irbuilderbpf.h000066400000000000000000000177301413460502400175710ustar00rootroot00000000000000#pragma once #include "ast.h" #include "bpftrace.h" #include "types.h" #include #include #include #if LLVM_VERSION_MAJOR >= 5 && LLVM_VERSION_MAJOR < 7 #define CREATE_MEMCPY(dst, src, size, algn) \ CreateMemCpy((dst), (src), (size), (algn)) #define CREATE_MEMCPY_VOLATILE(dst, src, size, algn) \ CreateMemCpy((dst), (src), (size), (algn), true) #elif LLVM_VERSION_MAJOR >= 7 && LLVM_VERSION_MAJOR < 10 #define CREATE_MEMCPY(dst, src, size, algn) \ CreateMemCpy((dst), (algn), (src), (algn), (size)) #define CREATE_MEMCPY_VOLATILE(dst, src, size, algn) \ CreateMemCpy((dst), (algn), (src), (algn), (size), true) #elif LLVM_VERSION_MAJOR >= 10 #define CREATE_MEMCPY(dst, src, size, algn) \ CreateMemCpy((dst), MaybeAlign(algn), (src), MaybeAlign(algn), (size)) #define CREATE_MEMCPY_VOLATILE(dst, src, size, algn) \ CreateMemCpy((dst), MaybeAlign(algn), (src), MaybeAlign(algn), (size), true) #else #error Unsupported LLVM version #endif #if LLVM_VERSION_MAJOR >= 10 #define CREATE_MEMSET(ptr, val, size, align) \ CreateMemSet((ptr), (val), (size), MaybeAlign((align))) #else #define CREATE_MEMSET(ptr, val, size, align) \ CreateMemSet((ptr), (val), (size), (align)) #endif namespace bpftrace { namespace ast { using namespace llvm; class IRBuilderBPF : public IRBuilder<> { public: IRBuilderBPF(LLVMContext &context, Module &module, BPFtrace &bpftrace); 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(llvm::Type *ty, llvm::Value *arraysize, const std::string &name=""); AllocaInst *CreateAllocaBPF(const SizedType &stype, llvm::Value *arraysize, const std::string &name=""); AllocaInst *CreateAllocaBPF(int bytes, const std::string &name=""); llvm::Type *GetType(const SizedType &stype); llvm::ConstantInt *GetIntSameSize(uint64_t C, llvm::Value *expr); llvm::ConstantInt *GetIntSameSize(uint64_t C, llvm::Type *ty); CallInst *CreateBpfPseudoCallId(int mapid); CallInst *CreateBpfPseudoCallId(Map &map); CallInst *CreateBpfPseudoCallValue(int mapid); CallInst *CreateBpfPseudoCallValue(Map &map); Value *CreateMapLookupElem(Value *ctx, Map &map, Value *key, const location &loc); Value *CreateMapLookupElem(Value *ctx, int mapid, Value *key, SizedType &type, const location &loc); void CreateMapUpdateElem(Value *ctx, Map &map, Value *key, Value *val, const location &loc); void CreateMapDeleteElem(Value *ctx, Map &map, Value *key, const location &loc); void CreateProbeRead(Value *ctx, Value *dst, size_t size, Value *src, AddrSpace as, const location &loc); void CreateProbeRead(Value *ctx, Value *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc); CallInst *CreateProbeReadStr(Value *ctx, AllocaInst *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc); CallInst *CreateProbeReadStr(Value *ctx, AllocaInst *dst, size_t size, Value *src, AddrSpace as, const location &loc); CallInst *CreateProbeReadStr(Value *ctx, Value *dst, size_t size, Value *src, AddrSpace as, const location &loc); Value *CreateUSDTReadArgument(Value *ctx, AttachPoint *attach_point, int usdt_location_index, int arg_name, Builtin &builtin, pid_t pid, AddrSpace as, const location &loc); Value *CreateStrncmp(Value *val1, Value *val2, uint64_t n, bool inverse); CallInst *CreateGetNs(bool boot_time); CallInst *CreateGetPidTgid(); CallInst *CreateGetCurrentCgroupId(); CallInst *CreateGetUidGid(); CallInst *CreateGetCpuId(); CallInst *CreateGetCurrentTask(); CallInst *CreateGetRandom(); CallInst *CreateGetStackId(Value *ctx, bool ustack, StackType stack_type, const location& loc); CallInst *CreateGetJoinMap(Value *ctx, const location& loc); CallInst *createCall(Value *callee, ArrayRef args, const Twine &Name); void CreateGetCurrentComm(Value *ctx, AllocaInst *buf, size_t size, const location& loc); void CreatePerfEventOutput(Value *ctx, Value *data, size_t size); void CreateSignal(Value *ctx, Value *sig, const location &loc); void CreateOverrideReturn(Value *ctx, Value *rc); void CreateHelperError(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location& loc); void CreateHelperErrorCond(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location& loc, bool compare_zero=false); StructType *GetStructType(std::string name, const std::vector & elements, bool packed = false); AllocaInst *CreateUSym(llvm::Value *val); Value *CreateRegisterRead(Value *ctx, const std::string &builtin); Value *CreatKFuncArg(Value *ctx, SizedType& type, std::string& name); void CreatePath(Value *ctx, AllocaInst *buf, Value *path, const location &loc); void CreateSeqPrintf(Value *ctx, Value *fmt, Value *fmt_size, AllocaInst *data, Value *data_len, const location &loc); 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); int helper_error_id_ = 0; private: Module &module_; BPFtrace &bpftrace_; Value *CreateUSDTReadArgument(Value *ctx, struct bcc_usdt_argument *argument, Builtin &builtin, AddrSpace as, const location &loc); CallInst *createMapLookup(int mapid, Value *key); Constant *createProbeReadStrFn(llvm::Type *dst, llvm::Type *src, AddrSpace as); libbpf::bpf_func_id selectProbeReadHelper(AddrSpace as, bool str); std::map structs_; }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/pass_manager.cpp000066400000000000000000000025611413460502400201070ustar00rootroot00000000000000#include #include #include "bpftrace.h" #include "passes/printer.h" namespace bpftrace { namespace ast { namespace { void print(Node *root, const std::string &name, std::ostream &out) { out << "\nAST after: " << name << std::endl; out << "-------------------\n"; ast::Printer printer(out, true); printer.print(root); out << std::endl; } } // namespace void PassManager::AddPass(Pass p) { passes_.push_back(std::move(p)); } PassResult PassManager::Run(std::unique_ptr node, PassContext &ctx) { Node *root = node.release(); if (bt_debug != DebugLevel::kNone) print(root, "parser", std::cout); for (auto &pass : passes_) { auto result = pass.Run(*root, ctx); if (!result.Ok()) return result; if (result.Root()) { delete root; root = result.Root(); } if (bt_debug != DebugLevel::kNone) print(root, pass.name, std::cout); } return PassResult::Success(root); } PassResult PassResult::Error(const std::string &pass) { return PassResult(pass); } PassResult PassResult::Error(const std::string &pass, int code) { return PassResult(pass, code); } PassResult PassResult::Error(const std::string &pass, const std::string &msg) { return PassResult(pass, msg); } PassResult PassResult::Success(Node *root) { return PassResult(root); } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/pass_manager.h000066400000000000000000000047101413460502400175520ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include namespace bpftrace { class BPFtrace; namespace ast { class Node; class SemanticAnalyser; class Pass; /** Result of a pass run */ class PassResult { public: static PassResult Error(const std::string &pass); static PassResult Error(const std::string &pass, int code); static PassResult Error(const std::string &pass, const std::string &msg); static PassResult Success(Node *root = nullptr); // Ok returns whether the pass was successful or not bool Ok() const { return success_; }; const std::optional GetErrorMsg() { return errmsg_; }; const std::optional GetErrorCode() { return errcode_; } /** Return the pass in which the failure occurred */ const std::optional GetErrorPass() { return errpass_; } Node *Root() const { return root_; }; private: PassResult(const std::string &pass) : success_(false), errpass_(pass) { } PassResult(const std::string &pass, int errcode) : success_(false), errpass_(pass), errcode_(errcode) { } PassResult(const std::string &pass, const std::string &msg) : success_(false), errpass_(pass), errmsg_(msg) { } PassResult(const std::string &pass, int errcode, const std::string &msg) : success_(false), errpass_(pass), errcode_(errcode), errmsg_(msg) { } PassResult(Node *root) : success_(true), root_(root) { } bool success_ = false; std::optional errpass_; std::optional errcode_; std::optional errmsg_; Node *root_ = nullptr; }; /** Context/config for passes Note: Most state should end up in the BPFtrace class instead of here */ struct PassContext { public: PassContext(BPFtrace &b) : b(b){}; BPFtrace &b; }; using PassFPtr = std::function; /* Base pass */ class Pass { public: Pass() = delete; Pass(std::string name, PassFPtr fn) : fn_(fn), name(name){}; virtual ~Pass() = default; PassResult Run(Node &root, PassContext &ctx) { return fn_(root, ctx); }; private: PassFPtr fn_; public: std::string name; }; class PassManager { public: PassManager() = default; void AddPass(Pass p); [[nodiscard]] PassResult Run(std::unique_ptr n, PassContext &ctx); private: std::vector passes_; }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/000077500000000000000000000000001413460502400162355ustar00rootroot00000000000000bpftrace-0.14.0/src/ast/passes/callback_visitor.h000066400000000000000000000026271413460502400217300ustar00rootroot00000000000000#pragma once #include "visitors.h" #include namespace bpftrace { namespace ast { using callback = std::function; class CallbackVisitor : public Visitor { public: explicit CallbackVisitor(callback func) : func_(func) { } void visit(Integer &integer) override; void visit(PositionalParameter ¶m) override; void visit(String &string) override; void visit(StackMode &mode) override; void visit(Identifier &identifier) override; void visit(Builtin &builtin) override; void visit(Call &call) override; void visit(Map &map) override; void visit(Variable &var) override; void visit(Binop &binop) override; void visit(Unop &unop) override; void visit(Ternary &ternary) override; void visit(FieldAccess &acc) override; void visit(ArrayAccess &arr) override; void visit(Cast &cast) override; void visit(Tuple &tuple) override; void visit(ExprStatement &expr) override; void visit(AssignMapStatement &assignment) override; void visit(AssignVarStatement &assignment) override; void visit(If &if_block) override; void visit(Unroll &unroll) override; void visit(While &while_block) override; void visit(Jump &jump) override; void visit(Predicate &pred) override; void visit(AttachPoint &ap) override; void visit(Probe &probe) override; void visit(Program &program) override; private: callback func_; }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/codegen_llvm.cpp000066400000000000000000003224411413460502400214050ustar00rootroot00000000000000#include "codegen_llvm.h" #include "arch/arch.h" #include "ast.h" #include "ast/async_event_types.h" #include "ast/bpforc/bpforc.h" #include "codegen_helper.h" #include "log.h" #include "signal_bt.h" #include "tracepoint_format_parser.h" #include "types.h" #include "usdt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace bpftrace { namespace ast { CodegenLLVM::CodegenLLVM(Node *root, BPFtrace &bpftrace) : root_(root), bpftrace_(bpftrace), orc_(BpfOrc::Create()), module_(std::make_unique("bpftrace", orc_->getContext())), b_(orc_->getContext(), *module_.get(), bpftrace) { module_->setDataLayout(datalayout()); module_->setTargetTriple(LLVMTargetTriple); } void CodegenLLVM::visit(Integer &integer) { expr_ = b_.getInt64(integer.n); } void CodegenLLVM::visit(PositionalParameter ¶m) { switch (param.ptype) { case PositionalParameterType::positional: { std::string pstr = bpftrace_.get_param(param.n, param.is_in_str); if (!param.is_in_str) { expr_ = b_.getInt64(std::stoll(pstr, nullptr, 0)); } else { Constant *const_str = ConstantDataArray::getString(module_->getContext(), pstr, true); AllocaInst *buf = b_.CreateAllocaBPF(ArrayType::get(b_.getInt8Ty(), pstr.length() + 1), "str"); b_.CREATE_MEMSET(buf, b_.getInt8(0), pstr.length() + 1, 1); b_.CreateStore(const_str, buf); expr_ = b_.CreatePtrToInt(buf, b_.getInt64Ty()); expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } } break; case PositionalParameterType::count: expr_ = b_.getInt64(bpftrace_.num_params()); break; } } void CodegenLLVM::visit(String &string) { string.str.resize(string.type.GetSize() - 1); Constant *const_str = ConstantDataArray::getString(module_->getContext(), string.str, true); AllocaInst *buf = b_.CreateAllocaBPF(string.type, "str"); b_.CreateStore(const_str, buf); expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } // NB: we do not resolve identifiers that are structs. That is because in // bpftrace you cannot really instantiate a struct. void CodegenLLVM::visit(Identifier &identifier) { if (bpftrace_.enums_.count(identifier.ident) != 0) { expr_ = b_.getInt64(bpftrace_.enums_[identifier.ident]); } else { LOG(FATAL) << "unknown identifier \"" << identifier.ident << "\""; } } void CodegenLLVM::kstack_ustack(const std::string &ident, StackType stack_type, const location &loc) { Value *stackid = b_.CreateGetStackId( ctx_, ident == "ustack", stack_type, loc); // Kernel stacks should not be differentiated by tid, 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 and so we do usym()-style packing. if (ident == "ustack") { // pack uint64_t with: (uint32_t)stack_id, (uint32_t)pid Value *pidhigh = b_.CreateShl(b_.CreateGetPidTgid(), 32); stackid = b_.CreateOr(stackid, pidhigh); } expr_ = stackid; } void CodegenLLVM::visit(Builtin &builtin) { if (builtin.ident == "nsecs") { expr_ = b_.CreateGetNs(bpftrace_.feature_->has_helper_ktime_get_boot_ns()); } else if (builtin.ident == "elapsed") { AllocaInst *key = b_.CreateAllocaBPF(b_.getInt64Ty(), "elapsed_key"); b_.CreateStore(b_.getInt64(0), key); auto *map = bpftrace_.maps[MapManager::Type::Elapsed].value(); auto type = CreateUInt64(); auto start = b_.CreateMapLookupElem(ctx_, map->id, key, type, builtin.loc); expr_ = b_.CreateGetNs(bpftrace_.feature_->has_helper_ktime_get_boot_ns()); expr_ = b_.CreateSub(expr_, start); // start won't be on stack, no need to LifeTimeEnd it b_.CreateLifetimeEnd(key); } else if (builtin.ident == "kstack" || builtin.ident == "ustack") { kstack_ustack(builtin.ident, builtin.type.stack_type, builtin.loc); } else if (builtin.ident == "pid" || builtin.ident == "tid") { Value *pidtgid = b_.CreateGetPidTgid(); if (builtin.ident == "pid") { expr_ = b_.CreateLShr(pidtgid, 32); } else if (builtin.ident == "tid") { expr_ = b_.CreateAnd(pidtgid, 0xffffffff); } } else if (builtin.ident == "cgroup") { expr_ = b_.CreateGetCurrentCgroupId(); } else if (builtin.ident == "uid" || builtin.ident == "gid" || builtin.ident == "username") { Value *uidgid = b_.CreateGetUidGid(); if (builtin.ident == "uid" || builtin.ident == "username") { expr_ = b_.CreateAnd(uidgid, 0xffffffff); } else if (builtin.ident == "gid") { expr_ = b_.CreateLShr(uidgid, 32); } } else if (builtin.ident == "cpu") { expr_ = b_.CreateGetCpuId(); } else if (builtin.ident == "curtask") { expr_ = b_.CreateGetCurrentTask(); } else if (builtin.ident == "rand") { expr_ = b_.CreateGetRandom(); } else if (builtin.ident == "comm") { AllocaInst *buf = b_.CreateAllocaBPF(builtin.type, "comm"); // initializing memory needed for older kernels: b_.CREATE_MEMSET(buf, b_.getInt8(0), builtin.type.GetSize(), 1); b_.CreateGetCurrentComm(ctx_, buf, builtin.type.GetSize(), builtin.loc); expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } else if ((!builtin.ident.compare(0, 3, "arg") && builtin.ident.size() == 4 && builtin.ident.at(3) >= '0' && builtin.ident.at(3) <= '9') || builtin.ident == "retval" || builtin.ident == "func") { if (builtin.type.is_funcarg) { expr_ = b_.CreatKFuncArg(ctx_, builtin.type, builtin.ident); return; } if (builtin.ident.find("arg") != std::string::npos && probetype(current_attach_point_->provider) == ProbeType::usdt) { expr_ = b_.CreateUSDTReadArgument(ctx_, current_attach_point_, current_usdt_location_index_, atoi(builtin.ident.substr(3).c_str()), builtin, bpftrace_.pid(), AddrSpace::user, builtin.loc); return; } expr_ = b_.CreateRegisterRead(ctx_, builtin.ident); if (builtin.type.IsUsymTy()) { expr_ = b_.CreateUSym(expr_); Value *expr = expr_; expr_deleter_ = [this, expr]() { b_.CreateLifetimeEnd(expr); }; } } else if (!builtin.ident.compare(0, 4, "sarg") && builtin.ident.size() == 5 && builtin.ident.at(4) >= '0' && builtin.ident.at(4) <= '9') { int sp_offset = arch::sp_offset(); if (sp_offset == -1) { LOG(FATAL) << "negative offset for stack pointer"; } int arg_num = atoi(builtin.ident.substr(4).c_str()); Value *ctx = b_.CreatePointerCast(ctx_, b_.getInt64Ty()->getPointerTo()); Value *sp = b_.CreateLoad(b_.getInt64Ty(), b_.CreateGEP(ctx, b_.getInt64(sp_offset)), "reg_sp"); dyn_cast(sp)->setVolatile(true); AllocaInst *dst = b_.CreateAllocaBPF(builtin.type, builtin.ident); Value *src = b_.CreateAdd(sp, b_.getInt64((arg_num + arch::arg_stack_offset()) * sizeof(uintptr_t))); b_.CreateProbeRead(ctx_, dst, 8, src, builtin.type.GetAS(), builtin.loc); expr_ = b_.CreateLoad(dst); b_.CreateLifetimeEnd(dst); } else if (builtin.ident == "probe") { auto begin = bpftrace_.resources.probe_ids.begin(); auto end = bpftrace_.resources.probe_ids.end(); auto found = std::find(begin, end, probefull_); builtin.probe_id = std::distance(begin, found); if (found == end) { bpftrace_.resources.probe_ids.push_back(probefull_); } expr_ = b_.getInt64(builtin.probe_id); } else if (builtin.ident == "args" || builtin.ident == "ctx") { // ctx is undocumented builtin: for debugging // ctx_ is casted to int for arithmetic operation // it will be casted to a pointer when loading expr_ = b_.CreatePtrToInt(ctx_, b_.getInt64Ty()); } else if (builtin.ident == "cpid") { pid_t cpid = bpftrace_.child_->pid(); if (cpid < 1) { LOG(FATAL) << "BUG: Invalid cpid: " << cpid; } expr_ = b_.getInt64(cpid); } else { LOG(FATAL) << "unknown builtin \"" << builtin.ident << "\""; } } void CodegenLLVM::visit(Call &call) { if (call.func == "count") { Map &map = *call.map; auto [key, scoped_key_deleter] = getMapKey(map); Value *oldval = b_.CreateMapLookupElem(ctx_, map, key, call.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_val"); b_.CreateStore(b_.CreateAdd(oldval, b_.getInt64(1)), newval); b_.CreateMapUpdateElem(ctx_, map, key, newval, call.loc); // oldval can only be an integer so won't be in memory and doesn't need lifetime end b_.CreateLifetimeEnd(newval); expr_ = nullptr; } else if (call.func == "sum") { Map &map = *call.map; auto [key, scoped_key_deleter] = getMapKey(map); Value *oldval = b_.CreateMapLookupElem(ctx_, map, key, call.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_val"); auto scoped_del = accept(call.vargs->front()); // promote int to 64-bit expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), call.vargs->front()->type.IsSigned()); b_.CreateStore(b_.CreateAdd(expr_, oldval), newval); b_.CreateMapUpdateElem(ctx_, map, key, newval, call.loc); // oldval can only be an integer so won't be in memory and doesn't need lifetime end b_.CreateLifetimeEnd(newval); expr_ = nullptr; } else if (call.func == "min") { Map &map = *call.map; auto [key, scoped_key_deleter] = getMapKey(map); Value *oldval = b_.CreateMapLookupElem(ctx_, map, key, call.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_val"); // Store the max of (0xffffffff - val), so that our SGE comparison with uninitialized // elements will always store on the first occurrence. Revent this later when printing. Function *parent = b_.GetInsertBlock()->getParent(); auto scoped_del = accept(call.vargs->front()); // promote int to 64-bit expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), call.vargs->front()->type.IsSigned()); Value *inverted = b_.CreateSub(b_.getInt64(0xffffffff), expr_); BasicBlock *lt = BasicBlock::Create(module_->getContext(), "min.lt", parent); BasicBlock *ge = BasicBlock::Create(module_->getContext(), "min.ge", parent); b_.CreateCondBr(b_.CreateICmpSGE(inverted, oldval), ge, lt); b_.SetInsertPoint(ge); b_.CreateStore(inverted, newval); b_.CreateMapUpdateElem(ctx_, map, key, newval, call.loc); b_.CreateBr(lt); b_.SetInsertPoint(lt); b_.CreateLifetimeEnd(newval); expr_ = nullptr; } else if (call.func == "max") { Map &map = *call.map; auto [key, scoped_key_deleter] = getMapKey(map); Value *oldval = b_.CreateMapLookupElem(ctx_, map, key, call.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_val"); Function *parent = b_.GetInsertBlock()->getParent(); auto scoped_del = accept(call.vargs->front()); // promote int to 64-bit expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), call.vargs->front()->type.IsSigned()); BasicBlock *lt = BasicBlock::Create(module_->getContext(), "min.lt", parent); BasicBlock *ge = BasicBlock::Create(module_->getContext(), "min.ge", parent); b_.CreateCondBr(b_.CreateICmpSGE(expr_, oldval), ge, lt); b_.SetInsertPoint(ge); b_.CreateStore(expr_, newval); b_.CreateMapUpdateElem(ctx_, map, key, newval, call.loc); b_.CreateBr(lt); b_.SetInsertPoint(lt); b_.CreateLifetimeEnd(newval); expr_ = nullptr; } else if (call.func == "avg" || call.func == "stats") { // avg stores the count and total in a hist map using indexes 0 and 1 // respectively, and the calculation is made when printing. Map &map = *call.map; AllocaInst *count_key = getHistMapKey(map, b_.getInt64(0)); Value *count_old = b_.CreateMapLookupElem(ctx_, map, count_key, call.loc); AllocaInst *count_new = b_.CreateAllocaBPF(map.type, map.ident + "_num"); b_.CreateStore(b_.CreateAdd(count_old, b_.getInt64(1)), count_new); b_.CreateMapUpdateElem(ctx_, map, count_key, count_new, call.loc); b_.CreateLifetimeEnd(count_key); b_.CreateLifetimeEnd(count_new); AllocaInst *total_key = getHistMapKey(map, b_.getInt64(1)); Value *total_old = b_.CreateMapLookupElem(ctx_, map, total_key, call.loc); AllocaInst *total_new = b_.CreateAllocaBPF(map.type, map.ident + "_val"); auto scoped_del = accept(call.vargs->front()); // promote int to 64-bit expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), call.vargs->front()->type.IsSigned()); b_.CreateStore(b_.CreateAdd(expr_, total_old), total_new); b_.CreateMapUpdateElem(ctx_, map, total_key, total_new, call.loc); b_.CreateLifetimeEnd(total_key); b_.CreateLifetimeEnd(total_new); expr_ = nullptr; } else if (call.func == "hist") { if (!log2_func_) log2_func_ = createLog2Function(); Map &map = *call.map; auto scoped_del = accept(call.vargs->front()); // promote int to 64-bit expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), call.vargs->front()->type.IsSigned()); Value *log2 = b_.CreateCall(log2_func_, expr_, "log2"); AllocaInst *key = getHistMapKey(map, log2); Value *oldval = b_.CreateMapLookupElem(ctx_, map, key, call.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_val"); b_.CreateStore(b_.CreateAdd(oldval, b_.getInt64(1)), newval); b_.CreateMapUpdateElem(ctx_, map, key, newval, call.loc); // oldval can only be an integer so won't be in memory and doesn't need lifetime end b_.CreateLifetimeEnd(key); b_.CreateLifetimeEnd(newval); expr_ = nullptr; } else if (call.func == "lhist") { if (!linear_func_) linear_func_ = createLinearFunction(); Map &map = *call.map; auto scoped_del = accept(call.vargs->front()); // prepare arguments auto *value_arg = call.vargs->at(0); auto *min_arg = call.vargs->at(1); auto *max_arg = call.vargs->at(2); auto *step_arg = call.vargs->at(3); Value *value, *min, *max, *step; auto scoped_del_value_arg = accept(value_arg); value = expr_; auto scoped_del_min_arg = accept(min_arg); min = expr_; auto scoped_del_max_arg = accept(max_arg); max = expr_; auto scoped_del_step_arg = accept(step_arg); step = expr_; // promote int to 64-bit value = b_.CreateIntCast(value, b_.getInt64Ty(), call.vargs->front()->type.IsSigned()); min = b_.CreateIntCast(min, b_.getInt64Ty(), false); max = b_.CreateIntCast(max, b_.getInt64Ty(), false); step = b_.CreateIntCast(step, b_.getInt64Ty(), false); Value *linear = b_.CreateCall(linear_func_, { value, min, max, step }, "linear"); AllocaInst *key = getHistMapKey(map, linear); Value *oldval = b_.CreateMapLookupElem(ctx_, map, key, call.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_val"); b_.CreateStore(b_.CreateAdd(oldval, b_.getInt64(1)), newval); b_.CreateMapUpdateElem(ctx_, map, key, newval, call.loc); // oldval can only be an integer so won't be in memory and doesn't need lifetime end b_.CreateLifetimeEnd(key); b_.CreateLifetimeEnd(newval); expr_ = nullptr; } else if (call.func == "delete") { auto &arg = *call.vargs->at(0); auto &map = static_cast(arg); auto [key, scoped_key_deleter] = getMapKey(map); auto imap = *bpftrace_.maps.Lookup(map.ident); if (!imap->is_clearable()) { // store zero insted of calling bpf_map_delete_elem() AllocaInst *val = b_.CreateAllocaBPF(map.type, map.ident + "_zero"); b_.CreateStore(Constant::getNullValue(b_.GetType(map.type)), val); b_.CreateMapUpdateElem(ctx_, map, key, val, call.loc); b_.CreateLifetimeEnd(val); } else { b_.CreateMapDeleteElem(ctx_, map, key, call.loc); } expr_ = nullptr; } else if (call.func == "str") { AllocaInst *strlen = b_.CreateAllocaBPF(b_.getInt64Ty(), "strlen"); b_.CREATE_MEMSET(strlen, b_.getInt8(0), sizeof(uint64_t), 1); if (call.vargs->size() > 1) { auto scoped_del = accept(call.vargs->at(1)); Value *proposed_strlen = b_.CreateAdd(expr_, b_.getInt64(1)); // add 1 to accommodate probe_read_str's null byte // largest read we'll allow = our global string buffer size Value *max = b_.getInt64(bpftrace_.strlen_); // 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, max, "str.min.cmp"); // select proposed_strlen if it's sufficiently low, otherwise choose maximum Value *Select = b_.CreateSelect(Cmp, proposed_strlen, max, "str.min.select"); b_.CreateStore(Select, strlen); } else { b_.CreateStore(b_.getInt64(bpftrace_.strlen_), strlen); } AllocaInst *buf = b_.CreateAllocaBPF(bpftrace_.strlen_, "str"); b_.CREATE_MEMSET(buf, b_.getInt8(0), bpftrace_.strlen_, 1); auto arg0 = call.vargs->front(); auto scoped_del = accept(call.vargs->front()); b_.CreateProbeReadStr( ctx_, buf, b_.CreateLoad(strlen), expr_, arg0->type.GetAS(), call.loc); b_.CreateLifetimeEnd(strlen); expr_ = buf; expr_deleter_ = [this,buf]() { b_.CreateLifetimeEnd(buf); }; } else if (call.func == "buf") { Value *max_length = b_.getInt64(bpftrace_.strlen_); size_t fixed_buffer_length = bpftrace_.strlen_; Value *length; if (call.vargs->size() > 1) { auto &arg = *call.vargs->at(1); auto scoped_del = accept(&arg); Value *proposed_length = expr_; Value *cmp = b_.CreateICmp( CmpInst::ICMP_ULE, proposed_length, max_length, "length.cmp"); length = b_.CreateSelect( cmp, proposed_length, max_length, "length.select"); if (arg.is_literal) fixed_buffer_length = *bpftrace_.get_int_literal(&arg); } else { auto &arg = *call.vargs->at(0); fixed_buffer_length = arg.type.GetNumElements() * arg.type.GetElementTy()->GetSize(); length = b_.getInt8(fixed_buffer_length); } 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, false); AllocaInst *buf = b_.CreateAllocaBPF(buf_struct, "buffer"); Value *buf_len_offset = b_.CreateGEP(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, { b_.getInt32(0), b_.getInt32(1) }); b_.CREATE_MEMSET(buf_data_offset, b_.GetIntSameSize(0, elements.at(0)), fixed_buffer_length, 1); auto scoped_del = accept(call.vargs->front()); auto arg0 = call.vargs->front(); // arg0 is already on the bpf stack -> use probe kernel // otherwise -> addrspace of arg0->type // case : struct MyStruct { char b[4]; }; // $s = (struct MyStruct *)arg0; buf($s->b, 4) b_.CreateProbeRead(ctx_, static_cast(buf_data_offset), length, expr_, find_addrspace_stack(arg0->type), call.loc); expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } else if (call.func == "path") { AllocaInst *buf = b_.CreateAllocaBPF(bpftrace_.strlen_, "path"); b_.CREATE_MEMSET(buf, b_.getInt8(0), bpftrace_.strlen_, 1); call.vargs->front()->accept(*this); b_.CreatePath(ctx_, buf, expr_, call.loc); expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } else if (call.func == "kaddr") { uint64_t addr; auto name = bpftrace_.get_string_literal(call.vargs->at(0)); addr = bpftrace_.resolve_kname(name); if (!addr) throw std::runtime_error("Failed to resolve kernel symbol: " + name); expr_ = b_.getInt64(addr); } else if (call.func == "uaddr") { auto name = bpftrace_.get_string_literal(call.vargs->at(0)); struct symbol sym = {}; int err = bpftrace_.resolve_uname(name, &sym, current_attach_point_->target); if (err < 0 || sym.address == 0) throw std::runtime_error("Could not resolve symbol: " + current_attach_point_->target + ":" + name); expr_ = b_.getInt64(sym.address); } else if (call.func == "cgroupid") { uint64_t cgroupid; auto path = bpftrace_.get_string_literal(call.vargs->at(0)); cgroupid = bpftrace_.resolve_cgroupid(path); expr_ = b_.getInt64(cgroupid); } else if (call.func == "join") { auto arg0 = call.vargs->front(); auto scoped_del = accept(arg0); auto addrspace = arg0->type.GetAS(); AllocaInst *first = b_.CreateAllocaBPF(b_.getInt64Ty(), call.func + "_first"); AllocaInst *second = b_.CreateAllocaBPF(b_.getInt64Ty(), call.func + "_second"); Value *perfdata = b_.CreateGetJoinMap(ctx_, call.loc); Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *zero = BasicBlock::Create(module_->getContext(), "joinzero", parent); BasicBlock *notzero = BasicBlock::Create(module_->getContext(), "joinnotzero", parent); b_.CreateCondBr(b_.CreateICmpNE(perfdata, ConstantExpr::getCast(Instruction::IntToPtr, b_.getInt64(0), b_.getInt8PtrTy()), "joinzerocond"), notzero, zero); // arg0 b_.SetInsertPoint(notzero); b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::join)), perfdata); b_.CreateStore(b_.getInt64(join_id_), b_.CreateGEP(perfdata, b_.getInt64(8))); join_id_++; AllocaInst *arr = b_.CreateAllocaBPF(b_.getInt64Ty(), call.func + "_r0"); b_.CreateProbeRead(ctx_, arr, 8, expr_, addrspace, call.loc); b_.CreateProbeReadStr(ctx_, b_.CreateAdd(perfdata, b_.getInt64(8 + 8)), bpftrace_.join_argsize_, b_.CreateLoad(arr), addrspace, call.loc); for (unsigned int i = 1; i < bpftrace_.join_argnum_; i++) { // argi b_.CreateStore(b_.CreateAdd(expr_, b_.getInt64(8 * i)), first); b_.CreateProbeRead( ctx_, second, 8, b_.CreateLoad(first), addrspace, call.loc); b_.CreateProbeReadStr( ctx_, b_.CreateAdd(perfdata, b_.getInt64(8 + 8 + i * bpftrace_.join_argsize_)), bpftrace_.join_argsize_, b_.CreateLoad(second), addrspace, call.loc); } // emit b_.CreatePerfEventOutput( ctx_, perfdata, 8 + 8 + bpftrace_.join_argnum_ * bpftrace_.join_argsize_); b_.CreateBr(zero); // done b_.SetInsertPoint(zero); expr_ = nullptr; } else if (call.func == "ksym") { // We want expr_ to just pass through from the child node - don't set it here auto scoped_del = accept(call.vargs->front()); } else if (call.func == "usym") { auto scoped_del = accept(call.vargs->front()); expr_ = b_.CreateUSym(expr_); } 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_t", elements, false); AllocaInst *buf = b_.CreateAllocaBPF(inet_struct, "inet"); Value *af_offset = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) }); Value *af_type; auto inet = call.vargs->at(0); if (call.vargs->size() == 1) { if (inet->type.IsIntegerTy() || inet->type.GetSize() == 4) { af_type = b_.getInt64(AF_INET); } else { af_type = b_.getInt64(AF_INET6); } } else { inet = call.vargs->at(1); auto scoped_del = accept(call.vargs->at(0)); af_type = b_.CreateIntCast(expr_, b_.getInt64Ty(), true); } b_.CreateStore(af_type, af_offset); Value *inet_offset = b_.CreateGEP(buf, {b_.getInt32(0), b_.getInt32(1)}); b_.CREATE_MEMSET(inet_offset, b_.getInt8(0), 16, 1); auto scoped_del = accept(inet); if (inet->type.IsArrayTy() || inet->type.IsStringTy()) { b_.CreateProbeRead(ctx_, static_cast(inet_offset), inet->type.GetSize(), expr_, inet->type.GetAS(), call.loc); } else { b_.CreateStore(b_.CreateIntCast(expr_, b_.getInt32Ty(), false), b_.CreatePointerCast(inet_offset, b_.getInt32Ty()->getPointerTo())); } expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } else if (call.func == "reg") { auto reg_name = bpftrace_.get_string_literal(call.vargs->at(0)); int offset = arch::offset(reg_name); if (offset == -1) { LOG(FATAL) << "negative offset on reg() call"; } Value *ctx = b_.CreatePointerCast(ctx_, b_.getInt64Ty()->getPointerTo()); expr_ = b_.CreateLoad(b_.getInt64Ty(), b_.CreateGEP(ctx, b_.getInt64(offset)), call.func + "_" + reg_name); dyn_cast(expr_)->setVolatile(true); } else if (call.func == "printf") { // We overload printf call for iterator probe's seq_printf helper. if (probetype(current_attach_point_->provider) == ProbeType::iter) { auto mapid = bpftrace_.maps[MapManager::Type::SeqPrintfData].value()->id; 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 = CreateBuffer(nargs * 8); AllocaInst *data = b_.CreateAllocaBPFInit(data_type, "data"); for (size_t i = 1; i < call.vargs->size(); i++) { // process argument expression Expression &arg = *call.vargs->at(i); auto scoped_del = accept(&arg); // and store it to data area Value *offset = b_.CreateGEP( data, { b_.getInt64(0), b_.getInt64((i - 1) * ptr_size) }); b_.CreateStore(expr_, offset); // keep the expression alive, so it's still there // for following seq_printf call expr_deleter_ = scoped_del.disarm(); data_size += ptr_size; } // pick to current format string auto ids = bpftrace_.resources.seq_printf_ids.at(seq_printf_id_); auto idx = std::get<0>(ids); auto size = std::get<1>(ids); // and load it from the map Value *map_data = b_.CreateBpfPseudoCallValue(mapid); Value *fmt = b_.CreateAdd(map_data, b_.getInt64(idx)); // and finally the seq_printf call b_.CreateSeqPrintf( ctx_, fmt, b_.getInt64(size), data, b_.getInt64(data_size), call.loc); seq_printf_id_++; } else { createFormatStringCall(call, printf_id_, bpftrace_.resources.printf_args, "printf", AsyncAction::printf); } } else if (call.func == "system") { createFormatStringCall(call, system_id_, bpftrace_.resources.system_args, "system", AsyncAction::syscall); } else if (call.func == "cat") { createFormatStringCall( call, cat_id_, bpftrace_.resources.cat_args, "cat", AsyncAction::cat); } else if (call.func == "exit") { /* * perf event output has: uint64_t asyncaction_id * The asyncaction_id informs user-space that this is not a printf(), but is a * special asynchronous action. The ID maps to exit(). */ AllocaInst *perfdata = b_.CreateAllocaBPF(b_.getInt64Ty(), "perfdata"); b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::exit)), perfdata); b_.CreatePerfEventOutput(ctx_, perfdata, sizeof(uint64_t)); b_.CreateLifetimeEnd(perfdata); expr_ = nullptr; 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); } else if (call.func == "print") { if (call.vargs->at(0)->is_map) createPrintMapCall(call); else createPrintNonMapCall(call, non_map_print_id_); } else if (call.func == "clear" || call.func == "zero") { auto elements = AsyncEvent::MapEvent().asLLVMType(b_); StructType *event_struct = b_.GetStructType(call.func + "_t", elements, true); auto &arg = *call.vargs->at(0); auto &map = static_cast(arg); AllocaInst *buf = b_.CreateAllocaBPF(event_struct, call.func + "_" + map.ident); auto aa_ptr = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) }); if (call.func == "clear") b_.CreateStore(b_.GetIntSameSize(asyncactionint(AsyncAction::clear), elements.at(0)), aa_ptr); else b_.CreateStore(b_.GetIntSameSize(asyncactionint(AsyncAction::zero), elements.at(0)), aa_ptr); auto id = bpftrace_.maps[map.ident].value()->id; auto *ident_ptr = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) }); b_.CreateStore(b_.GetIntSameSize(id, elements.at(1)), ident_ptr); b_.CreatePerfEventOutput(ctx_, buf, getStructSize(event_struct)); b_.CreateLifetimeEnd(buf); expr_ = nullptr; } else if (call.func == "time") { auto elements = AsyncEvent::Time().asLLVMType(b_); StructType *time_struct = b_.GetStructType(call.func + "_t", elements, true); AllocaInst *buf = b_.CreateAllocaBPF(time_struct, call.func + "_t"); b_.CreateStore(b_.GetIntSameSize(asyncactionint(AsyncAction::time), elements.at(0)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore(b_.GetIntSameSize(time_id_, elements.at(1)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); time_id_++; b_.CreatePerfEventOutput(ctx_, buf, getStructSize(time_struct)); b_.CreateLifetimeEnd(buf); expr_ = nullptr; } 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(strftime_id_, elements.at(0)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); strftime_id_++; Expression *arg = call.vargs->at(1); auto scoped_del = accept(arg); b_.CreateStore(expr_, b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); expr_ = buf; } else if (call.func == "kstack" || call.func == "ustack") { kstack_ustack(call.func, call.type.stack_type, call.loc); } else if (call.func == "signal") { // int bpf_send_signal(u32 sig) auto &arg = *call.vargs->at(0); if (arg.type.IsStringTy()) { auto signame = bpftrace_.get_string_literal(&arg); int sigid = signal_name_to_num(signame); // Should be caught in semantic analyser if (sigid < 1) { LOG(FATAL) << "BUG: Invalid signal ID for \"" << signame << "\""; } b_.CreateSignal(ctx_, b_.getInt32(sigid), call.loc); return; } auto scoped_del = accept(&arg); expr_ = b_.CreateIntCast(expr_, b_.getInt32Ty(), arg.type.IsSigned()); b_.CreateSignal(ctx_, expr_, call.loc); } else if (call.func == "sizeof") { expr_ = b_.getInt64(call.vargs->at(0)->type.GetSize()); } else if (call.func == "strncmp") { uint64_t size = (uint64_t)*bpftrace_.get_int_literal(call.vargs->at(2)); const auto& left_arg = call.vargs->at(0); const auto& right_arg = call.vargs->at(1); auto left_string = getString(left_arg); auto right_string = getString(right_arg); expr_ = b_.CreateStrncmp( left_string.first, right_string.first, size, false); } else if (call.func == "override") { // int bpf_override(struct pt_regs *regs, u64 rc) // returns: 0 auto &arg = *call.vargs->at(0); auto scoped_del = accept(&arg); expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), arg.type.IsSigned()); b_.CreateOverrideReturn(ctx_, expr_); } else if (call.func == "kptr" || call.func == "uptr") { auto arg = call.vargs->at(0); auto scoped_del = accept(arg); } else if (call.func == "macaddr") { // MAC addresses are presented as char[6] AllocaInst *buf = b_.CreateAllocaBPFInit(call.type, "macaddr"); auto macaddr = call.vargs->front(); auto scoped_del = accept(macaddr); if (onStack(macaddr->type)) b_.CREATE_MEMCPY(buf, expr_, macaddr->type.GetSize(), 1); else b_.CreateProbeRead(ctx_, static_cast(buf), macaddr->type.GetSize(), expr_, macaddr->type.GetAS(), call.loc); expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } else if (call.func == "unwatch") { Expression *addr = call.vargs->at(0); addr->accept(*this); auto elements = AsyncEvent::WatchpointUnwatch().asLLVMType(b_); StructType *unwatch_struct = b_.GetStructType("unwatch_t", elements, true); AllocaInst *buf = b_.CreateAllocaBPF(unwatch_struct, "unwatch"); size_t struct_size = datalayout().getTypeAllocSize(unwatch_struct); b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::watchpoint_detach)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore( b_.CreateIntCast(expr_, b_.getInt64Ty(), false /* unsigned */), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); b_.CreatePerfEventOutput(ctx_, buf, struct_size); b_.CreateLifetimeEnd(buf); expr_ = nullptr; } else { LOG(FATAL) << "missing codegen for function \"" << call.func << "\""; } } void CodegenLLVM::visit(Map &map) { auto [key, scoped_key_deleter] = getMapKey(map); Value *value = b_.CreateMapLookupElem(ctx_, map, key, map.loc); expr_ = value; if (dyn_cast(value)) expr_deleter_ = [this, value]() { b_.CreateLifetimeEnd(value); }; } void CodegenLLVM::visit(Variable &var) { // Arrays and structs are not memcopied for local variables if (needMemcpy(var.type) && !(var.type.IsArrayTy() || var.type.IsRecordTy())) { expr_ = variables_[var.ident]; } else { expr_ = b_.CreateLoad(variables_[var.ident]); } } std::pair CodegenLLVM::getString(Expression *expr) { std::pair result; if (expr->is_literal) { auto str = bpftrace_.get_string_literal(expr); result.first = ConstantDataArray::getString(module_->getContext(), str); result.second = str.size() + 1; } else { auto scoped_del = accept(expr); result.first = expr_; result.second = expr->type.GetSize(); expr_deleter_ = scoped_del.disarm(); } return result; } void CodegenLLVM::binop_string(Binop &binop) { if (binop.op != Operator::EQ && binop.op != Operator::NE) { LOG(FATAL) << "missing codegen to string operator \"" << opstr(binop) << "\""; } std::string string_literal; // strcmp returns 0 when strings are equal bool inverse = binop.op == Operator::EQ; auto left_string = getString(binop.left); auto right_string = getString(binop.right); size_t len = std::min(left_string.second, right_string.second); expr_ = b_.CreateStrncmp(left_string.first, right_string.first, len, inverse); } void CodegenLLVM::binop_buf(Binop &binop) { if (binop.op != Operator::EQ && binop.op != Operator::NE) { LOG(FATAL) << "missing codegen to buffer operator \"" << opstr(binop) << "\""; } std::string string_literal(""); // strcmp returns 0 when strings are equal bool inverse = binop.op == Operator::EQ; auto scoped_del_right = accept(binop.right); Value *right_string = expr_; auto scoped_del_left = accept(binop.left); Value *left_string = expr_; size_t len = std::min(binop.left->type.GetSize(), binop.right->type.GetSize()); expr_ = b_.CreateStrncmp(left_string, right_string, len, inverse); } void CodegenLLVM::binop_int(Binop &binop) { Value *lhs, *rhs; auto scoped_del_left = accept(binop.left); lhs = expr_; auto scoped_del_right = accept(binop.right); rhs = expr_; // 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 untill str() is accepted. // Extend the liftime of the buffer if (dynamic_cast(binop.left)) expr_deleter_ = scoped_del_left.disarm(); if (dynamic_cast(binop.right)) expr_deleter_ = scoped_del_right.disarm(); bool lsign = binop.left->type.IsSigned(); bool rsign = binop.right->type.IsSigned(); bool do_signed = lsign && rsign; // promote int to 64-bit lhs = b_.CreateIntCast(lhs, b_.getInt64Ty(), lsign); rhs = b_.CreateIntCast(rhs, b_.getInt64Ty(), rsign); switch (binop.op) { case Operator::EQ: expr_ = b_.CreateICmpEQ(lhs, rhs); break; case Operator::NE: expr_ = b_.CreateICmpNE(lhs, rhs); break; case Operator::LE: { expr_ = do_signed ? b_.CreateICmpSLE(lhs, rhs) : b_.CreateICmpULE(lhs, rhs); break; } case Operator::GE: { expr_ = do_signed ? b_.CreateICmpSGE(lhs, rhs) : b_.CreateICmpUGE(lhs, rhs); break; } case Operator::LT: { expr_ = do_signed ? b_.CreateICmpSLT(lhs, rhs) : b_.CreateICmpULT(lhs, rhs); break; } case Operator::GT: { expr_ = do_signed ? b_.CreateICmpSGT(lhs, rhs) : b_.CreateICmpUGT(lhs, rhs); break; } case Operator::LEFT: expr_ = b_.CreateShl(lhs, rhs); break; case Operator::RIGHT: expr_ = b_.CreateLShr(lhs, rhs); break; case Operator::PLUS: expr_ = b_.CreateAdd(lhs, rhs); break; case Operator::MINUS: expr_ = b_.CreateSub(lhs, rhs); break; case Operator::MUL: expr_ = b_.CreateMul(lhs, rhs); break; case Operator::DIV: expr_ = b_.CreateUDiv(lhs, rhs); break; 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). expr_ = b_.CreateURem(lhs, rhs); break; } case Operator::BAND: expr_ = b_.CreateAnd(lhs, rhs); break; case Operator::BOR: expr_ = b_.CreateOr(lhs, rhs); break; case Operator::BXOR: expr_ = b_.CreateXor(lhs, rhs); break; default: LOG(FATAL) << "BUG: \"" << opstr(binop) << "\" was handled earlier"; } // Using signed extension will result in -1 which will likely confuse users expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), false); } void 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(FATAL) << "BUG: binop_ptr: op not implemented for type\"" << opstr(binop) << "\""; break; case Operator::PLUS: case Operator::MINUS: arith = true; break; default: LOG(FATAL) << "BUG: binop_ptr invalid op \"" << opstr(binop) << "\""; } auto scoped_del_left = accept(binop.left); Value *lhs = expr_; auto scoped_del_right = accept(binop.right); Value *rhs = expr_; // note: the semantic phase blocks invalid combinations if (compare) { switch (binop.op) { case Operator::EQ: expr_ = b_.CreateICmpEQ(lhs, rhs); break; case Operator::NE: expr_ = b_.CreateICmpNE(lhs, rhs); break; case Operator::LE: { expr_ = b_.CreateICmpULE(lhs, rhs); break; } case Operator::GE: { expr_ = b_.CreateICmpUGE(lhs, rhs); break; } case Operator::LT: { expr_ = b_.CreateICmpULT(lhs, rhs); break; } case Operator::GT: { expr_ = b_.CreateICmpUGT(lhs, rhs); break; } default: LOG(FATAL) << "BUG: invalid op \"" << opstr(binop) << "\""; } } else if (arith) { // Cannot use GEP here as LLVM doesn't know its a pointer bool leftptr = binop.left->type.IsPtrTy(); auto &ptr = leftptr ? binop.left->type : binop.right->type; Value *ptr_expr = leftptr ? lhs : rhs; Value *other_expr = leftptr ? rhs : lhs; auto elem_size = b_.getInt64(ptr.GetPointeeTy()->GetSize()); expr_ = b_.CreateMul(elem_size, other_expr); if (binop.op == Operator::PLUS) expr_ = b_.CreateAdd(ptr_expr, expr_); else expr_ = b_.CreateSub(ptr_expr, expr_); } } void CodegenLLVM::visit(Binop &binop) { // Handle && and || separately so short circuiting works if (binop.op == Operator::LAND) { expr_ = createLogicalAnd(binop); return; } else if (binop.op == Operator::LOR) { expr_ = createLogicalOr(binop); return; } SizedType &type = binop.left->type; if (binop.left->type.IsPtrTy() || binop.right->type.IsPtrTy()) { binop_ptr(binop); } else if (type.IsStringTy()) { binop_string(binop); } else if (type.IsBufferTy()) { binop_buf(binop); } else { binop_int(binop); } } static bool unop_skip_accept(Unop &unop) { if (unop.expr->type.IsIntTy()) { if (unop.op == Operator::INCREMENT || unop.op == Operator::DECREMENT) return unop.expr->is_map || unop.expr->is_variable; } return false; } void CodegenLLVM::unop_int(Unop &unop) { SizedType &type = unop.expr->type; switch (unop.op) { case Operator::LNOT: { auto ty = expr_->getType(); Value *zero_value = Constant::getNullValue(ty); expr_ = b_.CreateICmpEQ(expr_, zero_value); // CreateICmpEQ() returns 1-bit integer // Cast it to the same type of the operand // Use unsigned extention, otherwise !0 becomes -1 expr_ = b_.CreateIntCast(expr_, ty, false); break; } case Operator::BNOT: expr_ = b_.CreateNot(expr_); break; case Operator::MINUS: expr_ = b_.CreateNeg(expr_); break; case Operator::INCREMENT: case Operator::DECREMENT: { createIncDec(unop); break; } case Operator::MUL: { // When dereferencing a 32-bit integer, only read in 32-bits, etc. int size = type.GetSize(); auto as = type.GetAS(); AllocaInst *dst = b_.CreateAllocaBPF(SizedType(type.type, size), "deref"); b_.CreateProbeRead(ctx_, dst, size, expr_, as, unop.loc); expr_ = b_.CreateIntCast(b_.CreateLoad(dst), b_.getInt64Ty(), type.IsSigned()); b_.CreateLifetimeEnd(dst); break; } default: LOG(FATAL) << "BUG: unop_int: invalid op \"" << opstr(unop) << "\""; } } void CodegenLLVM::unop_ptr(Unop &unop) { SizedType &type = unop.expr->type; switch (unop.op) { case Operator::MUL: { if (unop.type.IsIntegerTy() || unop.type.IsPtrTy()) { auto *et = type.GetPointeeTy(); // Pointer always 64 bits wide int size = unop.type.IsIntegerTy() ? et->GetIntBitWidth() / 8 : 8; AllocaInst *dst = b_.CreateAllocaBPF(*et, "deref"); b_.CreateProbeRead(ctx_, dst, size, expr_, type.GetAS(), unop.loc); expr_ = b_.CreateIntCast(b_.CreateLoad(dst), b_.getInt64Ty(), unop.type.IsSigned()); b_.CreateLifetimeEnd(dst); } break; } case Operator::INCREMENT: case Operator::DECREMENT: { createIncDec(unop); break; } default:; // Do nothing } } void CodegenLLVM::visit(Unop &unop) { auto scoped_del = ScopedExprDeleter(nullptr); if (!unop_skip_accept(unop)) scoped_del = accept(unop.expr); SizedType &type = unop.expr->type; if (type.IsIntegerTy()) { unop_int(unop); } else if (type.IsPtrTy()) { unop_ptr(unop); } else { LOG(FATAL) << "invalid type (" << type << ") passed to unary operator \"" << opstr(unop) << "\""; } } void CodegenLLVM::visit(Ternary &ternary) { 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 *result = ternary.type.IsNoneTy() ? nullptr : b_.CreateAllocaBPF(ternary.type, "result"); AllocaInst *buf = ternary.type.IsNoneTy() ? nullptr : b_.CreateAllocaBPF(ternary.type, "buf"); Value *cond; auto scoped_del = accept(ternary.cond); cond = expr_; Value *zero_value = Constant::getNullValue(cond->getType()); b_.CreateCondBr(b_.CreateICmpNE(cond, zero_value, "true_cond"), left_block, right_block); if (ternary.type.IsIntTy()) { // fetch selected integer via CreateStore b_.SetInsertPoint(left_block); auto scoped_del_left = accept(ternary.left); expr_ = b_.CreateIntCast(expr_, b_.GetType(ternary.type), ternary.type.IsSigned()); b_.CreateStore(expr_, result); b_.CreateBr(done); b_.SetInsertPoint(right_block); auto scoped_del_right = accept(ternary.right); expr_ = b_.CreateIntCast(expr_, b_.GetType(ternary.type), ternary.type.IsSigned()); b_.CreateStore(expr_, result); b_.CreateBr(done); b_.SetInsertPoint(done); expr_ = b_.CreateLoad(result); } else if (ternary.type.IsStringTy()) { // copy selected string via CreateMemCpy b_.SetInsertPoint(left_block); auto scoped_del_left = accept(ternary.left); b_.CREATE_MEMCPY(buf, expr_, ternary.type.GetSize(), 1); b_.CreateBr(done); b_.SetInsertPoint(right_block); auto scoped_del_right = accept(ternary.right); b_.CREATE_MEMCPY(buf, expr_, ternary.type.GetSize(), 1); b_.CreateBr(done); b_.SetInsertPoint(done); expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } else { // Type::none b_.SetInsertPoint(left_block); { auto scoped_del = accept(ternary.left); } b_.CreateBr(done); b_.SetInsertPoint(right_block); { auto scoped_del = accept(ternary.right); } b_.CreateBr(done); b_.SetInsertPoint(done); expr_ = nullptr; } } void CodegenLLVM::visit(FieldAccess &acc) { SizedType &type = acc.expr->type; AddrSpace addrspace = acc.expr->type.GetAS(); assert(type.IsRecordTy() || type.IsTupleTy()); auto scoped_del = accept(acc.expr); bool is_ctx = type.IsCtxAccess(); bool is_tparg = type.is_tparg; bool is_internal = type.is_internal; bool is_funcarg = type.is_funcarg; assert(type.IsRecordTy() || type.IsTupleTy()); if (type.is_funcarg) { auto probe_type = probetype(current_attach_point_->provider); if (probe_type == ProbeType::kfunc || probe_type == ProbeType::kretfunc) expr_ = b_.CreatKFuncArg(ctx_, acc.type, acc.field); else expr_ = b_.CreateRegisterRead(ctx_, "arg" + std::to_string(acc.type.funcarg_idx)); return; } else if (type.IsTupleTy()) { Value *src = b_.CreateGEP(expr_, { b_.getInt32(0), b_.getInt32(acc.index) }); SizedType &elem_type = type.GetFields()[acc.index].type; if (shouldBeOnStackAlready(elem_type)) { expr_ = src; // Extend lifetime of source buffer expr_deleter_ = scoped_del.disarm(); } else expr_ = b_.CreateLoad(b_.GetType(elem_type), src); return; } std::string cast_type = is_tparg ? tracepoint_struct_ : type.GetName(); // This overwrites the stored type! type = CreateRecord(cast_type, bpftrace_.structs.Lookup(cast_type)); if (is_ctx) type.MarkCtxAccess(); type.is_tparg = is_tparg; type.is_internal = is_internal; type.is_funcarg = is_funcarg; // Restore the addrspace info // struct MyStruct { const int* a; }; $s = (struct MyStruct *)arg0; $s->a type.SetAS(addrspace); auto &field = type.GetField(acc.field); if (onStack(type)) { readDatastructElemFromStack( expr_, b_.getInt64(field.offset), type, field.type, scoped_del); } else { // Structs may contain two kinds of fields that must be handled separately // (bitfields and _data_loc) if (field.type.IsIntTy() && (field.is_bitfield || field.is_data_loc)) { Value *src = b_.CreateAdd(expr_, b_.getInt64(field.offset)); if (field.is_bitfield) { Value *raw; if (type.IsCtxAccess()) raw = b_.CreateLoad( b_.CreateIntToPtr(src, b_.GetType(field.type)->getPointerTo()), true); else { AllocaInst *dst = b_.CreateAllocaBPF(field.type, type.GetName() + "." + acc.field); // memset so verifier doesn't complain about reading uninitialized // stack b_.CREATE_MEMSET(dst, b_.getInt8(0), field.type.GetSize(), 1); b_.CreateProbeRead( ctx_, dst, field.bitfield.read_bytes, src, type.GetAS(), acc.loc); raw = b_.CreateLoad(dst); 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); expr_ = 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()); assert(ctx_->getType() == b_.getInt8PtrTy()); // 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. expr_ = b_.CreateLoad( b_.getInt32Ty(), b_.CreateGEP(b_.CreatePointerCast(ctx_, b_.getInt32Ty()->getPointerTo()), b_.getInt64(field.offset / 4))); expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), false); expr_ = b_.CreateAnd(expr_, b_.getInt64(0xFFFF)); expr_ = b_.CreateAdd(expr_, b_.CreatePtrToInt(ctx_, b_.getInt64Ty())); } } else { probereadDatastructElem(expr_, b_.getInt64(field.offset), type, field.type, scoped_del, acc.loc, type.GetName() + "." + acc.field); } } } void CodegenLLVM::visit(ArrayAccess &arr) { SizedType &type = arr.expr->type; auto elem_type = type.IsArrayTy() ? *type.GetElementTy() : *type.GetPointeeTy(); size_t elem_size = elem_type.GetSize(); auto scoped_del_expr = accept(arr.expr); Value *array = expr_; auto scoped_del_index = accept(arr.indexpr); if (onStack(type)) readDatastructElemFromStack(array, expr_, type, elem_type, scoped_del_expr); else { if (array->getType()->isPointerTy()) array = b_.CreatePtrToInt(array, b_.getInt64Ty()); Value *index = b_.CreateIntCast(expr_, b_.getInt64Ty(), type.IsSigned()); Value *offset = b_.CreateMul(index, b_.getInt64(elem_size)); probereadDatastructElem(array, offset, type, elem_type, scoped_del_expr, arr.loc, "array_access"); } } void CodegenLLVM::visit(Cast &cast) { auto scoped_del = accept(cast.expr); if (cast.type.IsIntTy()) { expr_ = b_.CreateIntCast(expr_, b_.getIntNTy(8 * cast.type.GetSize()), cast.type.IsSigned(), "cast"); } } 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(FATAL) << "BUG: Struct size mismatch: expected: " << our_size << ", real: " << llvm_size; } auto *layout = datalayout().getStructLayout( reinterpret_cast(llvm_type)); for (ssize_t i = 0; i < our_type.GetFieldCount(); i++) { ssize_t llvm_offset = layout->getElementOffset(i); auto &field = our_type.GetField(i); ssize_t our_offset = field.offset; if (llvm_offset != our_offset) { LOG(DEBUG) << "Struct offset mismatch for: " << field.type << "(" << i << ")" << ": (llvm) " << llvm_offset << " != " << our_offset; field.offset = llvm_offset; } } } void CodegenLLVM::visit(Tuple &tuple) { // Store elements on stack llvm::Type *tuple_ty = b_.GetType(tuple.type); compareStructure(tuple.type, tuple_ty); size_t tuple_size = datalayout().getTypeAllocSize(tuple_ty); AllocaInst *buf = b_.CreateAllocaBPF(tuple_ty, "tuple"); b_.CREATE_MEMSET(buf, b_.getInt8(0), tuple_size, 1); for (size_t i = 0; i < tuple.elems->size(); ++i) { Expression *elem = tuple.elems->at(i); auto scoped_del = accept(elem); Value *dst = b_.CreateGEP(buf, { b_.getInt32(0), b_.getInt32(i) }); if (onStack(elem->type)) b_.CREATE_MEMCPY(dst, expr_, elem->type.GetSize(), 1); else if (elem->type.IsArrayTy() || elem->type.IsRecordTy()) b_.CreateProbeRead(ctx_, dst, elem->type.GetSize(), expr_, elem->type.GetAS(), elem->loc); else b_.CreateStore(expr_, dst); } expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; } void CodegenLLVM::visit(ExprStatement &expr) { auto scoped_del = accept(expr.expr); } void CodegenLLVM::visit(AssignMapStatement &assignment) { Map &map = *assignment.map; auto scoped_del = accept(assignment.expr); bool self_alloca = false; if (!expr_) // Some functions do the assignments themselves return; Value *val, *expr; expr = expr_; auto [key, scoped_key_deleter] = getMapKey(map); if (shouldBeOnStackAlready(assignment.expr->type)) { val = expr; } else if (map.type.IsRecordTy() || map.type.IsArrayTy()) { if (assignment.expr->type.is_internal) { val = expr; } else { // 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 AllocaInst *dst = b_.CreateAllocaBPF(map.type, map.ident + "_val"); b_.CreateProbeRead(ctx_, dst, map.type.GetSize(), expr, assignment.expr->type.GetAS(), assignment.loc); val = dst; self_alloca = true; } } else if (map.type.IsPtrTy()) { // expr currently contains a pointer to the struct // and that's what we are saving AllocaInst *dst = b_.CreateAllocaBPF(map.type, map.ident + "_ptr"); b_.CreateStore(expr, dst); val = dst; self_alloca = true; } else { if (map.type.IsIntTy()) { // Integers are always stored as 64-bit in map values expr = b_.CreateIntCast(expr, b_.getInt64Ty(), map.type.IsSigned()); } val = b_.CreateAllocaBPF(map.type, map.ident + "_val"); b_.CreateStore(expr, val); self_alloca = true; } b_.CreateMapUpdateElem(ctx_, map, key, val, assignment.loc); if (self_alloca) b_.CreateLifetimeEnd(val); } void CodegenLLVM::visit(AssignVarStatement &assignment) { Variable &var = *assignment.var; auto scoped_del = accept(assignment.expr); if (variables_.find(var.ident) == variables_.end()) { SizedType alloca_type = var.type; // Arrays and structs need not to be copied when assigned to local variables // since they are treated as read-only - it is sufficient to assign // the pointer and do the memcpy/proberead later when necessary if (var.type.IsArrayTy() || var.type.IsRecordTy()) { auto &pointee_type = var.type.IsArrayTy() ? *var.type.GetElementTy() : var.type; alloca_type = CreatePointer(pointee_type, var.type.GetAS()); } AllocaInst *val = b_.CreateAllocaBPFInit(alloca_type, var.ident); variables_[var.ident] = val; } if (var.type.IsArrayTy() || var.type.IsRecordTy()) { // For arrays and structs, only the pointer is stored b_.CreateStore(b_.CreatePtrToInt(expr_, b_.getInt64Ty()), variables_[var.ident]); // Extend lifetime of RHS up to the end of probe scoped_del.disarm(); } else if (needMemcpy(var.type)) { b_.CREATE_MEMCPY(variables_[var.ident], expr_, var.type.GetSize(), 1); } else { b_.CreateStore(expr_, variables_[var.ident]); } } void CodegenLLVM::visit(If &if_block) { 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_del = accept(if_block.cond); Value *zero_value = Constant::getNullValue(expr_->getType()); Value *cond = b_.CreateICmpNE(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_block.else_stmts) { // 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); for (Statement *stmt : *if_block.stmts) auto scoped_del = accept(stmt); b_.CreateBr(if_end); b_.SetInsertPoint(if_end); if (if_block.else_stmts) { b_.SetInsertPoint(if_else); for (Statement *stmt : *if_block.else_stmts) auto scoped_del = accept(stmt); b_.CreateBr(if_end); b_.SetInsertPoint(if_end); } } void CodegenLLVM::visit(Unroll &unroll) { for (int i=0; i < unroll.var; i++) { // Make sure to save/restore async ID state b/c we could be processing // the same async calls multiple times. auto reset_ids = create_reset_ids(); for (Statement *stmt : *unroll.stmts) { auto scoped_del = accept(stmt); } reset_ids(); } } void CodegenLLVM::visit(Jump &jump) { switch (jump.ident) { case JumpType::RETURN: // return can be used outside of loops 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(FATAL) << "BUG: jump: invalid op \"" << opstr(jump) << "\""; } // 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 // Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *unreach = BasicBlock::Create(module_->getContext(), "unreach", parent); b_.SetInsertPoint(unreach); } void CodegenLLVM::visit(While &while_block) { if (!loop_metadata_) loop_metadata_ = createLoopMetadata(); Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *while_cond = BasicBlock::Create(module_->getContext(), "while_cond", parent); BasicBlock *while_body = BasicBlock::Create(module_->getContext(), "while_body", parent); BasicBlock *while_end = BasicBlock::Create(module_->getContext(), "while_end", parent); loops_.push_back(std::make_tuple(while_cond, while_end)); b_.CreateBr(while_cond); b_.SetInsertPoint(while_cond); auto scoped_del = accept(while_block.cond); Value *zero_value = Constant::getNullValue(expr_->getType()); auto *cond = b_.CreateICmpNE(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); for (Statement *stmt : *while_block.stmts) { auto scoped_del = accept(stmt); } b_.CreateBr(while_cond); b_.SetInsertPoint(while_end); loops_.pop_back(); } void CodegenLLVM::visit(Predicate &pred) { 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_del = accept(pred.expr); // allow unop casts in predicates: expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), false); expr_ = b_.CreateICmpEQ(expr_, b_.getInt64(0), "predcond"); b_.CreateCondBr(expr_, pred_false_block, pred_true_block); b_.SetInsertPoint(pred_false_block); createRet(); b_.SetInsertPoint(pred_true_block); } void CodegenLLVM::visit(AttachPoint &) { // Empty } void CodegenLLVM::generateProbe(Probe &probe, const std::string &full_func_id, const std::string §ion_name, FunctionType *func_type, bool expansion, std::optional usdt_location_index, bool dummy) { // tracepoint wildcard expansion, part 3 of 3. Set tracepoint_struct_ for use // by args builtin. if (probetype(current_attach_point_->provider) == ProbeType::tracepoint) tracepoint_struct_ = TracepointFormatParser::get_struct_name(full_func_id); int index = getNextIndexForProbe(probe.name()); if (expansion) current_attach_point_->set_index(full_func_id, index); else probe.set_index(index); Function *func = Function::Create( func_type, Function::ExternalLinkage, section_name, module_.get()); func->setSection( get_section_name_for_probe(section_name, index, usdt_location_index)); 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 (probe.pred) { auto scoped_del = accept(probe.pred); } variables_.clear(); for (Statement *stmt : *probe.stmts) { auto scoped_del = accept(stmt); } createRet(); if (dummy) { func->eraseFromParent(); return; } auto pt = probetype(current_attach_point_->provider); if ((pt == ProbeType::watchpoint || pt == ProbeType::asyncwatchpoint) && current_attach_point_->func.size()) generateWatchpointSetupProbe( func_type, section_name, current_attach_point_->address, index); } void CodegenLLVM::createRet(Value *value) { // If value is explicitly provided, use it if (value) { b_.CreateRet(value); return; } // Fall back to default return value switch (probetype(current_attach_point_->provider)) { case ProbeType::invalid: LOG(FATAL) << "Returning from invalid probetype"; break; 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. b_.CreateRet(b_.getInt64(1)); break; 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::kfunc: case ProbeType::kretfunc: case ProbeType::iter: b_.CreateRet(b_.getInt64(0)); break; } } void CodegenLLVM::visit(Probe &probe) { FunctionType *func_type = FunctionType::get( b_.getInt64Ty(), {b_.getInt8PtrTy()}, // struct pt_regs *ctx false); // Probe has at least one attach point (required by the parser) auto &attach_point = (*probe.attach_points)[0]; // All usdt probes need expansion to be able to read arguments if (probetype(attach_point->provider) == ProbeType::usdt) probe.need_expansion = true; bool generated = false; current_attach_point_ = attach_point; /* * Most of the time, we can take a probe like kprobe:do_f* and build a * single BPF program for that, called "s_kprobe:do_f*", and attach it to * each wildcard match. An exception is the "probe" builtin, where we need * to build different BPF programs for each wildcard match that cantains an * ID for the match. Those programs will be called "s_kprobe:do_fcntl" etc. */ if (probe.need_expansion == false) { // build a single BPF program pre-wildcards probefull_ = probe.name(); generateProbe(probe, probefull_, probefull_, func_type, false); generated = true; } else { /* * Build a separate BPF program for each wildcard match. * 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 = create_reset_ids(); for (auto attach_point : *probe.attach_points) { current_attach_point_ = attach_point; std::set matches; if (attach_point->provider == "BEGIN" || attach_point->provider == "END") { matches.insert(attach_point->provider); } else { matches = bpftrace_.probe_matcher_->get_matches_for_ap(*attach_point); } tracepoint_struct_ = ""; for (const auto &m : matches) { reset_ids(); std::string match = m; generated = true; // USDT probes must specify a target binary path, a provider, // and a function name. // So we will extract out the path and the provider namespace to get // just the function name. if (probetype(attach_point->provider) == ProbeType::usdt) { std::string func_id = match; std::string target = erase_prefix(func_id); std::string ns = erase_prefix(func_id); std::string orig_target = attach_point->target; std::string orig_ns = attach_point->ns; // Ensure that the full probe name used is the resolved one for this // probe. attach_point->target = target; attach_point->ns = ns; probefull_ = attach_point->name(func_id); // Set the probe identifier so that we can read arguments later auto usdt = USDTHelper::find(bpftrace_.pid(), target, ns, func_id); if (!usdt.has_value()) throw std::runtime_error("Failed to find usdt probe: " + probefull_); attach_point->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) current_usdt_location_index_ = 0; for (int i = 0; i < attach_point->usdt.num_locations; ++i) { reset_ids(); std::string full_func_id = match + "_loc" + std::to_string(i); generateProbe(probe, full_func_id, probefull_, func_type, true, i); current_usdt_location_index_++; } // Propagate the originally specified target and namespace in case // they contain a wildcard. attach_point->target = orig_target; attach_point->ns = orig_ns; } else { if (attach_point->provider == "BEGIN" || attach_point->provider == "END") probefull_ = attach_point->provider; else if ((probetype(attach_point->provider) == ProbeType::tracepoint || probetype(attach_point->provider) == ProbeType::uprobe || probetype(attach_point->provider) == ProbeType::uretprobe)) { // Tracepoint and uprobe probes must specify both a target // (tracepoint category) and a function name std::string func = match; std::string category = erase_prefix(func); probefull_ = attach_point->name(category, func); } else if (probetype(attach_point->provider) == ProbeType::watchpoint || probetype(attach_point->provider) == ProbeType::asyncwatchpoint) { // Watchpoint probes comes with target prefix. Strip the target to // get the function erase_prefix(match); probefull_ = attach_point->name(match); } else probefull_ = attach_point->name(match); generateProbe(probe, match, probefull_, func_type, true); } } } if (!generated) generateProbe( probe, "dummy", "dummy", func_type, false, std::nullopt, true); } if (generated) bpftrace_.add_probe(probe); current_attach_point_ = nullptr; } void CodegenLLVM::visit(Program &program) { for (Probe *probe : *program.probes) auto scoped_del = accept(probe); } int CodegenLLVM::getNextIndexForProbe(const std::string &probe_name) { if (next_probe_index_.count(probe_name) == 0) next_probe_index_[probe_name] = 1; int index = next_probe_index_[probe_name]; next_probe_index_[probe_name] += 1; return index; } std::tuple CodegenLLVM::getMapKey( Map &map) { Value *key; if (map.vargs) { // A single value as a map key (e.g., @[comm] = 0;) if (map.vargs->size() == 1) { Expression *expr = map.vargs->at(0); auto scoped_del = accept(expr); if (onStack(expr->type)) { key = expr_; // Call-ee freed scoped_del.disarm(); } else { key = b_.CreateAllocaBPF(expr->type, map.ident + "_key"); if (expr->type.IsArrayTy() || expr->type.IsRecordTy()) { // We need to read the entire array/struct and save it b_.CreateProbeRead(ctx_, key, expr->type.GetSize(), expr_, expr->type.GetAS(), expr->loc); } else { b_.CreateStore( b_.CreateIntCast(expr_, b_.getInt64Ty(), expr->type.IsSigned()), b_.CreatePointerCast(key, expr_->getType()->getPointerTo())); } } } else { // Two or more values as a map key (e.g, @[comm, pid] = 1;) key = getMultiMapKey(map, {}, 0); } } else { // No map key (e.g., @ = 1;). Use 0 as a key. key = b_.CreateAllocaBPF(CreateUInt64(), map.ident + "_key"); b_.CreateStore(b_.getInt64(0), key); } auto key_deleter = [this, key]() { if (dyn_cast(key)) b_.CreateLifetimeEnd(key); }; return std::make_tuple(key, ScopedExprDeleter(std::move(key_deleter))); } AllocaInst *CodegenLLVM::getMultiMapKey(Map &map, const std::vector &extra_keys, size_t extra_keys_size) { size_t size = extra_keys_size; for (Expression *expr : *map.vargs) { size += expr->type.GetSize(); } AllocaInst *key = b_.CreateAllocaBPF(size, map.ident + "_key"); int offset = 0; bool aligned = true; // Construct a map key in the stack for (Expression *expr : *map.vargs) { auto scoped_del = accept(expr); Value *offset_val = b_.CreateGEP(key, { b_.getInt64(0), b_.getInt64(offset) }); if (onStack(expr->type)) { b_.CREATE_MEMCPY(offset_val, expr_, expr->type.GetSize(), 1); if ((expr->type.GetSize() % 8) != 0) aligned = false; } else { if (expr->type.IsArrayTy() || expr->type.IsRecordTy()) { // Read the array/struct into the key b_.CreateProbeRead(ctx_, offset_val, expr->type.GetSize(), expr_, expr->type.GetAS(), expr->loc); if ((expr->type.GetSize() % 8) != 0) aligned = false; } else { // promote map key to 64-bit: Value *key_elem = b_.CreateIntCast(expr_, b_.getInt64Ty(), expr->type.IsSigned()); Value *dst_ptr = b_.CreatePointerCast(offset_val, expr_->getType()->getPointerTo()); if (aligned) b_.CreateStore(key_elem, dst_ptr); else b_.createAlignedStore(key_elem, dst_ptr, 1); } } offset += expr->type.GetSize(); } for (auto *extra_key : extra_keys) { Value *offset_val = b_.CreateGEP(key, { b_.getInt64(0), b_.getInt64(offset) }); if (aligned) b_.CreateStore(extra_key, offset_val); else b_.createAlignedStore(extra_key, offset_val, 1); } return key; } AllocaInst *CodegenLLVM::getHistMapKey(Map &map, Value *log2) { if (map.vargs) return getMultiMapKey(map, { log2 }, 8); AllocaInst *key = b_.CreateAllocaBPF(CreateUInt64(), map.ident + "_key"); b_.CreateStore(log2, key); return key; } Value *CodegenLLVM::createLogicalAnd(Binop &binop) { assert(binop.left->type.IsIntTy()); assert(binop.right->type.IsIntTy()); Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *lhs_true_block = BasicBlock::Create(module_->getContext(), "&&_lhs_true", parent); BasicBlock *true_block = BasicBlock::Create(module_->getContext(), "&&_true", parent); BasicBlock *false_block = BasicBlock::Create(module_->getContext(), "&&_false", parent); BasicBlock *merge_block = BasicBlock::Create(module_->getContext(), "&&_merge", parent); Value *result = b_.CreateAllocaBPF(b_.getInt64Ty(), "&&_result"); Value *lhs; auto scoped_del_left = accept(binop.left); lhs = expr_; b_.CreateCondBr(b_.CreateICmpNE(lhs, b_.GetIntSameSize(0, lhs), "lhs_true_cond"), lhs_true_block, false_block); b_.SetInsertPoint(lhs_true_block); Value *rhs; auto scoped_del_right = accept(binop.right); rhs = expr_; b_.CreateCondBr(b_.CreateICmpNE(rhs, b_.GetIntSameSize(0, rhs), "rhs_true_cond"), true_block, false_block); b_.SetInsertPoint(true_block); b_.CreateStore(b_.getInt64(1), result); b_.CreateBr(merge_block); b_.SetInsertPoint(false_block); b_.CreateStore(b_.getInt64(0), result); b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); return b_.CreateLoad(result); } Value *CodegenLLVM::createLogicalOr(Binop &binop) { assert(binop.left->type.IsIntTy()); assert(binop.right->type.IsIntTy()); Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *lhs_false_block = BasicBlock::Create(module_->getContext(), "||_lhs_false", parent); BasicBlock *false_block = BasicBlock::Create(module_->getContext(), "||_false", parent); BasicBlock *true_block = BasicBlock::Create(module_->getContext(), "||_true", parent); BasicBlock *merge_block = BasicBlock::Create(module_->getContext(), "||_merge", parent); Value *result = b_.CreateAllocaBPF(b_.getInt64Ty(), "||_result"); Value *lhs; auto scoped_del_left = accept(binop.left); lhs = expr_; b_.CreateCondBr(b_.CreateICmpNE(lhs, b_.GetIntSameSize(0, lhs), "lhs_true_cond"), true_block, lhs_false_block); b_.SetInsertPoint(lhs_false_block); Value *rhs; auto scoped_del_right = accept(binop.right); rhs = expr_; b_.CreateCondBr(b_.CreateICmpNE(rhs, b_.GetIntSameSize(0, rhs), "rhs_true_cond"), true_block, false_block); b_.SetInsertPoint(false_block); b_.CreateStore(b_.getInt64(0), result); b_.CreateBr(merge_block); b_.SetInsertPoint(true_block); b_.CreateStore(b_.getInt64(1), result); b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); return b_.CreateLoad(result); } Function *CodegenLLVM::createLog2Function() { auto ip = b_.saveIP(); // log2() returns a bucket index for the given value. Index 0 is for // values less than 0, index 1 is for 0, and indexes 2 onwards is the // power-of-2 histogram index. // // log2(int n) // { // int result = 0; // int shift; // if (n < 0) return result; // result++; // if (n == 0) return result; // result++; // for (int i = 4; i >= 0; i--) // { // shift = (v >= (1<<(1<> = shift; // result += shift; // } // return result; // } FunctionType *log2_func_type = FunctionType::get(b_.getInt64Ty(), {b_.getInt64Ty()}, false); Function *log2_func = Function::Create(log2_func_type, Function::InternalLinkage, "log2", module_.get()); log2_func->addFnAttr(Attribute::AlwaysInline); log2_func->setSection("helpers"); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", log2_func); b_.SetInsertPoint(entry); // setup n and result registers Value *arg = log2_func->arg_begin(); Value *n_alloc = b_.CreateAllocaBPF(CreateUInt64()); b_.CreateStore(arg, n_alloc); Value *result = b_.CreateAllocaBPF(CreateUInt64()); b_.CreateStore(b_.getInt64(0), result); // test for less than zero BasicBlock *is_less_than_zero = BasicBlock::Create(module_->getContext(), "hist.is_less_than_zero", log2_func); BasicBlock *is_not_less_than_zero = BasicBlock::Create(module_->getContext(), "hist.is_not_less_than_zero", log2_func); b_.CreateCondBr(b_.CreateICmpSLT(b_.CreateLoad(n_alloc), b_.getInt64(0)), is_less_than_zero, is_not_less_than_zero); b_.SetInsertPoint(is_less_than_zero); createRet(b_.CreateLoad(result)); b_.SetInsertPoint(is_not_less_than_zero); // test for equal to zero BasicBlock *is_zero = BasicBlock::Create(module_->getContext(), "hist.is_zero", log2_func); BasicBlock *is_not_zero = BasicBlock::Create(module_->getContext(), "hist.is_not_zero", log2_func); b_.CreateCondBr(b_.CreateICmpEQ(b_.CreateLoad(n_alloc), b_.getInt64(0)), is_zero, is_not_zero); b_.SetInsertPoint(is_zero); b_.CreateStore(b_.getInt64(1), result); createRet(b_.CreateLoad(result)); b_.SetInsertPoint(is_not_zero); // power-of-2 index, offset by +2 b_.CreateStore(b_.getInt64(2), result); for (int i = 4; i >= 0; i--) { Value *n = b_.CreateLoad(n_alloc); Value *shift = b_.CreateShl(b_.CreateIntCast(b_.CreateICmpSGE(b_.CreateIntCast(n, b_.getInt64Ty(), false), b_.getInt64(1 << (1<getFunction("log2"); } 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); Function *linear_func = Function::Create(linear_func_type, Function::InternalLinkage, "linear", module_.get()); linear_func->addFnAttr(Attribute::AlwaysInline); linear_func->setSection("helpers"); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", linear_func); b_.SetInsertPoint(entry); // pull in arguments Value *value_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *min_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *max_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *step_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *result_alloc = b_.CreateAllocaBPF(CreateUInt64()); b_.CreateStore(linear_func->arg_begin() + 0, value_alloc); b_.CreateStore(linear_func->arg_begin() + 1, min_alloc); b_.CreateStore(linear_func->arg_begin() + 2, max_alloc); b_.CreateStore(linear_func->arg_begin() + 3, step_alloc); Value *cmp = nullptr; // algorithm { Value *min = b_.CreateLoad(min_alloc); Value *val = b_.CreateLoad(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(max_alloc); Value *val = b_.CreateLoad(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(step_alloc); Value *min = b_.CreateLoad(min_alloc); Value *max = b_.CreateLoad(max_alloc); Value *div = b_.CreateUDiv(b_.CreateSub(max, min), step); b_.CreateStore(b_.CreateAdd(div, b_.getInt64(1)), result_alloc); createRet(b_.CreateLoad(result_alloc)); } b_.SetInsertPoint(le_max); { Value *step = b_.CreateLoad(step_alloc); Value *min = b_.CreateLoad(min_alloc); Value *val = b_.CreateLoad(value_alloc); Value *div3 = b_.CreateUDiv(b_.CreateSub(val, min), step); b_.CreateStore(b_.CreateAdd(div3, b_.getInt64(1)), result_alloc); createRet(b_.CreateLoad(result_alloc)); } b_.restoreIP(ip); return module_->getFunction("linear"); } MDNode *CodegenLLVM::createLoopMetadata() { // Create metadata to disable loop unrolling // // For legacy reasons, the first item of a loop metadata node must be // a self-reference. See https://llvm.org/docs/LangRef.html#llvm-loop LLVMContext &context = orc_->getContext(); MDNode *unroll_disable = MDNode::get( context, MDString::get(context, "llvm.loop.unroll.disable")); MDNode *loopid = MDNode::getDistinct(context, { unroll_disable, unroll_disable }); loopid->replaceOperandWith(0, loopid); return loopid; } void CodegenLLVM::createFormatStringCall(Call &call, int &id, CallArgs &call_args, const std::string &call_name, AsyncAction async_action) { /* * perf event output has: uint64_t id, vargs * The id maps to bpftrace_.*_args_, and is a way to define the * types and offsets of each of the arguments, and share that between BPF and * user-space for printing. */ std::vector elements = { b_.getInt64Ty() }; // ID auto &args = std::get<1>(call_args.at(id)); for (Field &arg : args) { llvm::Type *ty = b_.GetType(arg.type); elements.push_back(ty); } StructType *fmt_struct = StructType::create(elements, call_name + "_t", false); int struct_size = datalayout().getTypeAllocSize(fmt_struct); auto *struct_layout = datalayout().getStructLayout(fmt_struct); for (size_t i=0; igetElementOffset(i+1); // +1 for the id field } AllocaInst *fmt_args = b_.CreateAllocaBPF(fmt_struct, call_name + "_args"); // as the struct is not packed we need to memset it. b_.CREATE_MEMSET(fmt_args, b_.getInt8(0), struct_size, 1); Value *id_offset = b_.CreateGEP(fmt_args, {b_.getInt32(0), b_.getInt32(0)}); b_.CreateStore(b_.getInt64(id + asyncactionint(async_action)), id_offset); for (size_t i=1; isize(); i++) { Expression &arg = *call.vargs->at(i); auto scoped_del = accept(&arg); Value *offset = b_.CreateGEP(fmt_args, {b_.getInt32(0), b_.getInt32(i)}); if (needMemcpy(arg.type)) b_.CREATE_MEMCPY(offset, expr_, arg.type.GetSize(), 1); else if (arg.type.IsIntegerTy() && arg.type.GetSize() < 8) b_.CreateStore( b_.CreateIntCast(expr_, b_.getInt64Ty(), arg.type.IsSigned()), offset); else b_.CreateStore(expr_, offset); } id++; b_.CreatePerfEventOutput(ctx_, fmt_args, struct_size); b_.CreateLifetimeEnd(fmt_args); expr_ = nullptr; } void CodegenLLVM::generateWatchpointSetupProbe( FunctionType *func_type, const std::string &expanded_probe_name, int arg_num, int index) { Function *func = Function::Create(func_type, Function::ExternalLinkage, get_watchpoint_setup_probe_name( expanded_probe_name), module_.get()); func->setSection( get_section_name_for_watchpoint_setup(expanded_probe_name, index)); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", func); b_.SetInsertPoint(entry); // Send SIGSTOP to curtask if (!current_attach_point_->async) b_.CreateSignal(ctx_, b_.getInt32(SIGSTOP), current_attach_point_->loc); // Pull out function argument Value *ctx = func->arg_begin(); int offset = arch::arg_offset(arg_num); Value *addr = b_.CreateLoad( b_.getInt64Ty(), b_.CreateGEP(ctx, b_.getInt64(offset * sizeof(uintptr_t))), "arg" + std::to_string(arg_num)); // Tell userspace to setup the real watchpoint auto elements = AsyncEvent::Watchpoint().asLLVMType(b_); StructType *watchpoint_struct = b_.GetStructType("watchpoint_t", elements, true); AllocaInst *buf = b_.CreateAllocaBPF(watchpoint_struct, "watchpoint"); size_t struct_size = datalayout().getTypeAllocSize(watchpoint_struct); // Fill in perf event struct b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::watchpoint_attach)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore(b_.getInt64(watchpoint_id_), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); watchpoint_id_++; b_.CreateStore(addr, b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(2) })); b_.CreatePerfEventOutput(ctx, buf, struct_size); b_.CreateLifetimeEnd(buf); createRet(); } void CodegenLLVM::createPrintMapCall(Call &call) { auto elements = AsyncEvent::Print().asLLVMType(b_); StructType *print_struct = b_.GetStructType(call.func + "_t", elements, true); auto &arg = *call.vargs->at(0); auto &map = static_cast(arg); AllocaInst *buf = b_.CreateAllocaBPF(print_struct, call.func + "_" + map.ident); // store asyncactionid: b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::print)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); auto id = bpftrace_.maps[map.ident].value()->id; auto *ident_ptr = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) }); 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_del = accept(call.vargs->at(arg_idx)); b_.CreateStore(b_.CreateIntCast(expr_, elements.at(arg_idx), false), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(arg_idx + 1) })); } for (; arg_idx < 3; arg_idx++) { b_.CreateStore(b_.GetIntSameSize(0, elements.at(arg_idx)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(arg_idx + 1) })); } b_.CreatePerfEventOutput(ctx_, buf, getStructSize(print_struct)); b_.CreateLifetimeEnd(buf); expr_ = nullptr; } void CodegenLLVM::createPrintNonMapCall(Call &call, int &id) { auto &arg = *call.vargs->at(0); auto scoped_del = accept(&arg); auto elements = AsyncEvent::PrintNonMap().asLLVMType(b_, arg.type.GetSize()); std::ostringstream struct_name; struct_name << call.func << "_" << arg.type.type << "_" << arg.type.GetSize() << "_t"; StructType *print_struct = b_.GetStructType(struct_name.str(), elements, true); AllocaInst *buf = b_.CreateAllocaBPF(print_struct, struct_name.str()); size_t struct_size = datalayout().getTypeAllocSize(print_struct); // Store asyncactionid: b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::print_non_map)), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); // Store print id b_.CreateStore(b_.getInt64(id), b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); // Store content Value *content_offset = b_.CreateGEP(buf, { b_.getInt32(0), b_.getInt32(2) }); b_.CREATE_MEMSET(content_offset, b_.getInt8(0), arg.type.GetSize(), 1); if (needMemcpy(arg.type)) { if (onStack(arg.type)) b_.CREATE_MEMCPY(content_offset, expr_, arg.type.GetSize(), 1); else b_.CreateProbeRead(ctx_, content_offset, arg.type.GetSize(), expr_, arg.type.GetAS(), arg.loc); } else { auto ptr = b_.CreatePointerCast(content_offset, expr_->getType()->getPointerTo()); b_.CreateStore(expr_, ptr); } id++; b_.CreatePerfEventOutput(ctx_, buf, struct_size); b_.CreateLifetimeEnd(buf); expr_ = nullptr; } void CodegenLLVM::generate_ir() { assert(state_ == State::INIT); auto scoped_del = accept(root_); state_ = State::IR; } void CodegenLLVM::emit_elf(const std::string &filename) { assert(state_ == State::OPT); legacy::PassManager PM; #if LLVM_VERSION_MAJOR >= 10 auto type = llvm::CGFT_ObjectFile; #else auto type = llvm::TargetMachine::CGFT_ObjectFile; #endif #if LLVM_VERSION_MAJOR >= 7 std::error_code err; raw_fd_ostream out(filename, err); if (err) throw std::system_error(err.value(), std::generic_category(), "Failed to open: " + filename); if (orc_->getTargetMachine().addPassesToEmitFile(PM, out, nullptr, type)) throw std::runtime_error("Cannot emit a file of this type"); PM.run(*module_.get()); return; #else std::ofstream file(filename); if (!file.is_open()) throw std::system_error(errno, std::generic_category(), "Failed to open: " + filename); std::unique_ptr> buf(new SmallVector()); raw_svector_ostream out(*buf); if (orc_->getTargetMachine().addPassesToEmitFile( PM, out, type, true, nullptr)) throw std::runtime_error("Cannot emit a file of this type"); file.write(buf->data(), buf->size_in_bytes()); #endif } void CodegenLLVM::optimize() { assert(state_ == State::IR); PassManagerBuilder PMB; PMB.OptLevel = 3; legacy::PassManager PM; PM.add(createFunctionInliningPass()); /* * llvm < 4.0 needs * PM.add(createAlwaysInlinerPass()); * llvm >= 4.0 needs * PM.add(createAlwaysInlinerLegacyPass()); * use below 'stable' workaround */ LLVMAddAlwaysInlinerPass(reinterpret_cast(&PM)); PMB.populateModulePassManager(PM); PM.run(*module_.get()); state_ = State::OPT; } std::unique_ptr CodegenLLVM::emit(void) { assert(state_ == State::OPT); orc_->compile(move(module_)); state_ = State::DONE; #ifdef LLVM_ORC_V2 auto has_sym = [this](const std::string &s) { auto sym = orc_->lookup(s); return (sym && sym->getAddress()); }; for (const auto &probe : bpftrace_.resources.special_probes) { if (has_sym(probe.name) || has_sym(probe.orig_name)) return std::move(orc_); } for (const auto &probe : bpftrace_.resources.probes) { if (has_sym(probe.name) || has_sym(probe.orig_name)) return std::move(orc_); } #endif return std::move(orc_); } std::unique_ptr CodegenLLVM::compile(void) { generate_ir(); optimize(); return emit(); } void CodegenLLVM::DumpIR(void) { DumpIR(std::cout); } void CodegenLLVM::DumpIR(std::ostream &out) { assert(module_.get() != nullptr); raw_os_ostream os(out); module_->print(os, nullptr, false, true); } CodegenLLVM::ScopedExprDeleter CodegenLLVM::accept(Node *node) { expr_deleter_ = nullptr; node->accept(*this); auto deleter = std::move(expr_deleter_); expr_deleter_ = nullptr; return ScopedExprDeleter(deleter); } // 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 void CodegenLLVM::readDatastructElemFromStack(Value *src_data, Value *index, const SizedType &data_type, const SizedType &elem_type, ScopedExprDeleter &scoped_del) { // 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 if (src_data->getType()->isIntegerTy()) src_data = b_.CreateIntToPtr(src_data, b_.GetType(data_type)->getPointerTo()); Value *src = b_.CreateGEP(src_data, { b_.getInt32(0), index }); // It may happen that the result pointer type is not correct, in such case // do a typecast auto dst_type = b_.GetType(elem_type); if (src->getType() != dst_type->getPointerTo()) src = b_.CreatePointerCast(src, dst_type->getPointerTo()); if (elem_type.IsIntegerTy() || elem_type.IsPtrTy()) { // Load the correct type from src expr_ = b_.CreateLoad(src, true); } else { // The inner type is an aggregate - instead of copying it, just pass // the pointer and extend lifetime of the source data expr_ = src; expr_deleter_ = scoped_del.disarm(); } } // 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: // src_data (external) pointer to the entire 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 // scoped_del scoped deleter for the source structure // loc location of the element access (for proberead) // temp_name name of a temporary variable, if the function creates any void CodegenLLVM::probereadDatastructElem(Value *src_data, Value *offset, const SizedType &data_type, const SizedType &elem_type, ScopedExprDeleter &scoped_del, location loc, const std::string &temp_name) { Value *src = b_.CreateAdd(src_data, offset); auto dst_type = b_.GetType(elem_type); 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. expr_ = src; expr_deleter_ = scoped_del.disarm(); } else if (elem_type.IsStringTy() || elem_type.IsBufferTy()) { // Read data onto stack AllocaInst *dst = b_.CreateAllocaBPF(elem_type, temp_name); b_.CreateProbeRead( ctx_, dst, elem_type.GetSize(), src, data_type.GetAS(), loc); expr_ = dst; expr_deleter_ = [this, dst]() { b_.CreateLifetimeEnd(dst); }; } else { // Read data onto stack if (data_type.IsCtxAccess()) { expr_ = b_.CreateLoad(b_.CreateIntToPtr(src, dst_type->getPointerTo()), true); expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), elem_type.IsSigned()); // check context access for iter probes (required by kernel) if (probetype(current_attach_point_->provider) == ProbeType::iter) { 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 *expr = expr_; expr = b_.CreateIntCast(expr, b_.getInt64Ty(), false); expr = b_.CreateICmpEQ(expr, b_.getInt64(0), "predcond"); b_.CreateCondBr(expr, pred_false_block, pred_true_block); b_.SetInsertPoint(pred_false_block); createRet(); b_.SetInsertPoint(pred_true_block); } } else { AllocaInst *dst = b_.CreateAllocaBPF(elem_type, temp_name); b_.CreateProbeRead( ctx_, dst, elem_type.GetSize(), src, data_type.GetAS(), loc); expr_ = b_.CreateIntCast(b_.CreateLoad(dst), b_.getInt64Ty(), elem_type.IsSigned()); b_.CreateLifetimeEnd(dst); } } } void CodegenLLVM::createIncDec(Unop &unop) { bool is_increment = unop.op == Operator::INCREMENT; SizedType &type = unop.expr->type; uint64_t step = type.IsPtrTy() ? type.GetPointeeTy()->GetSize() : 1; if (unop.expr->is_map) { Map &map = static_cast(*unop.expr); auto [key, scoped_key_deleter] = getMapKey(map); Value *oldval = b_.CreateMapLookupElem(ctx_, map, key, unop.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_newval"); if (is_increment) b_.CreateStore(b_.CreateAdd(oldval, b_.GetIntSameSize(step, oldval)), newval); else b_.CreateStore(b_.CreateSub(oldval, b_.GetIntSameSize(step, oldval)), newval); b_.CreateMapUpdateElem(ctx_, map, key, newval, unop.loc); if (unop.is_post_op) expr_ = oldval; else expr_ = b_.CreateLoad(newval); b_.CreateLifetimeEnd(newval); } else if (unop.expr->is_variable) { Variable &var = static_cast(*unop.expr); Value *oldval = b_.CreateLoad(variables_[var.ident]); Value *newval; if (is_increment) newval = b_.CreateAdd(oldval, b_.GetIntSameSize(step, oldval)); else newval = b_.CreateSub(oldval, b_.GetIntSameSize(step, oldval)); b_.CreateStore(newval, variables_[var.ident]); if (unop.is_post_op) expr_ = oldval; else expr_ = newval; } else { LOG(FATAL) << "invalid expression passed to " << opstr(unop); } } std::function CodegenLLVM::create_reset_ids() { return [this, starting_printf_id = this->printf_id_, starting_cat_id = this->cat_id_, starting_system_id = this->system_id_, starting_time_id = this->time_id_, starting_strftime_id = this->strftime_id_, starting_join_id = this->join_id_, starting_helper_error_id = this->b_.helper_error_id_, starting_non_map_print_id = this->non_map_print_id_, starting_seq_printf_id = this->seq_printf_id_] { this->printf_id_ = starting_printf_id; this->cat_id_ = starting_cat_id; this->system_id_ = starting_system_id; this->time_id_ = starting_time_id; this->strftime_id_ = starting_strftime_id; this->join_id_ = starting_join_id; this->b_.helper_error_id_ = starting_helper_error_id; this->non_map_print_id_ = starting_non_map_print_id; this->seq_printf_id_ = starting_seq_printf_id; }; } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/codegen_llvm.h000066400000000000000000000172451413460502400210550ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "ast/bpforc/bpforc.h" #include "bpftrace.h" #include "irbuilderbpf.h" #include "location.hh" #include "map.h" #include "visitors.h" #include #include #include #include namespace bpftrace { namespace ast { using namespace llvm; using CallArgs = std::vector>>; class CodegenLLVM : public Visitor { public: explicit CodegenLLVM(Node *root, BPFtrace &bpftrace); void visit(Integer &integer) override; void visit(PositionalParameter ¶m) override; void visit(String &string) override; void visit(Identifier &identifier) override; void visit(Builtin &builtin) override; void visit(StackMode &) override { }; void visit(Call &call) override; void visit(Map &map) override; void visit(Variable &var) override; void visit(Binop &binop) override; void visit(Unop &unop) override; void visit(Ternary &ternary) override; void visit(FieldAccess &acc) override; void visit(ArrayAccess &arr) override; void visit(Cast &cast) override; void visit(Tuple &tuple) override; void visit(ExprStatement &expr) override; void visit(AssignMapStatement &assignment) override; void visit(AssignVarStatement &assignment) override; void visit(If &if_block) override; void visit(Unroll &unroll) override; void visit(While &while_block) override; void visit(Jump &jump) override; void visit(Predicate &pred) override; void visit(AttachPoint &ap) override; void visit(Probe &probe) override; void visit(Program &program) override; AllocaInst *getHistMapKey(Map &map, Value *log2); int getNextIndexForProbe(const std::string &probe_name); Value *createLogicalAnd(Binop &binop); Value *createLogicalOr(Binop &binop); // Exists to make calling from a debugger easier void DumpIR(void); void DumpIR(std::ostream &out); void createFormatStringCall(Call &call, int &id, CallArgs &call_args, const std::string &call_name, AsyncAction async_action); void createPrintMapCall(Call &call); void createPrintNonMapCall(Call &call, int &id); void generate_ir(void); void optimize(void); std::unique_ptr emit(void); void emit_elf(const std::string &filename); // Combine generate_ir, optimize and emit into one call std::unique_ptr compile(void); private: class ScopedExprDeleter { public: explicit ScopedExprDeleter(std::function deleter) { deleter_ = std::move(deleter); } ScopedExprDeleter(const ScopedExprDeleter &other) = delete; ScopedExprDeleter &operator=(const ScopedExprDeleter &other) = delete; ScopedExprDeleter(ScopedExprDeleter &&other) = default; ScopedExprDeleter &operator=(ScopedExprDeleter &&other) = default; ~ScopedExprDeleter() { if (deleter_) deleter_(); } std::function disarm() { auto ret = deleter_; deleter_ = nullptr; return ret; } private: std::function deleter_; }; // Generate a probe for `current_attach_point_` // // If `dummy` is passed, then code is generated but immediately thrown away. // This is used to progress state (eg. asyncids) in this class instance for // invalid probes that still need to be visited. void generateProbe(Probe &probe, const std::string &full_func_id, const std::string §ion_name, FunctionType *func_type, bool expansion, std::optional usdt_location_index = std::nullopt, bool dummy = false); [[nodiscard]] ScopedExprDeleter accept(Node *node); [[nodiscard]] std::tuple getMapKey(Map &map); AllocaInst *getMultiMapKey(Map &map, const std::vector &extra_keys, size_t extra_keys_size); void compareStructure(SizedType &our_type, llvm::Type *llvm_type); Function *createLog2Function(); Function *createLinearFunction(); MDNode *createLoopMetadata(); std::pair getString(Expression *expr); void binop_string(Binop &binop); void binop_buf(Binop &binop); void binop_int(Binop &binop); void binop_ptr(Binop &binop); void unop_int(Unop &unop); void unop_ptr(Unop &unop); void kstack_ustack(const std::string &ident, StackType stack_type, const location &loc); // Create return instruction // // If null, return value will depend on current attach point void createRet(Value *value = nullptr); // Every time we see a watchpoint that specifies a function + arg pair, we // generate a special "setup" probe that: // // * sends SIGSTOP to the tracee // * pulls out the function arg // * sends an asyncaction to the bpftrace runtime and specifies the arg value // and which of the "real" probes to attach to the addr in the arg // // We need a separate "setup" probe per probe because we hard code the index // of the "real" probe the setup probe is to be replaced by. void generateWatchpointSetupProbe(FunctionType *func_type, const std::string &expanded_probe_name, int arg_num, int index); void readDatastructElemFromStack(Value *src_data, Value *index, const SizedType &data_type, const SizedType &elem_type, ScopedExprDeleter &scoped_del); void probereadDatastructElem(Value *src_data, Value *offset, const SizedType &data_type, const SizedType &elem_type, ScopedExprDeleter &scoped_del, location loc, const std::string &temp_name); void createIncDec(Unop &unop); // Return a lambda that has captured-by-value CodegenLLVM's async id state // (ie `printf_id_`, `seq_printf_id_`, etc.). Running the returned lambda // will restore `CodegenLLVM`s async id state back to when this function was // first called. std::function create_reset_ids(); Node *root_ = nullptr; BPFtrace &bpftrace_; std::unique_ptr orc_; std::unique_ptr module_; IRBuilderBPF b_; const DataLayout &datalayout() const { return orc_->getDataLayout(); } Value *expr_ = nullptr; std::function expr_deleter_; // intentionally empty Value *ctx_; AttachPoint *current_attach_point_ = nullptr; std::string probefull_; std::string tracepoint_struct_; std::map next_probe_index_; // Used if there are duplicate USDT entries int current_usdt_location_index_{ 0 }; std::map variables_; int printf_id_ = 0; int seq_printf_id_ = 0; int time_id_ = 0; int cat_id_ = 0; int strftime_id_ = 0; uint64_t join_id_ = 0; int system_id_ = 0; int non_map_print_id_ = 0; uint64_t watchpoint_id_ = 0; Function *linear_func_ = nullptr; Function *log2_func_ = nullptr; MDNode *loop_metadata_ = nullptr; size_t getStructSize(StructType *s) { return datalayout().getTypeAllocSize(s); } std::vector> loops_; std::unordered_map probe_names_; enum class State { INIT, IR, OPT, DONE, }; State state_ = State::INIT; }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/field_analyser.cpp000066400000000000000000000172251413460502400217310ustar00rootroot00000000000000#include "field_analyser.h" #include "dwarf_parser.h" #include "log.h" #include "probe_matcher.h" #include #include namespace bpftrace { namespace ast { void FieldAnalyser::visit(Identifier &identifier) { bpftrace_.btf_set_.insert(identifier.ident); } void FieldAnalyser::visit(Builtin &builtin) { if (builtin.ident == "ctx") { switch (prog_type_) { case BPF_PROG_TYPE_KPROBE: bpftrace_.btf_set_.insert("struct pt_regs"); break; case BPF_PROG_TYPE_PERF_EVENT: bpftrace_.btf_set_.insert("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) { std::string it_struct; if (attach_func_ == "task") { it_struct = "struct bpf_iter__task"; } else if (attach_func_ == "task_file") { it_struct = "struct bpf_iter__task_file"; } if (!it_struct.empty()) { bpftrace_.btf_set_.insert(it_struct); type_ = it_struct; } } } else if (builtin.ident == "curtask") { type_ = "struct task_struct"; bpftrace_.btf_set_.insert(type_); } else if (builtin.ident == "args") { resolve_args(*probe_); has_builtin_args_ = true; } else if (builtin.ident == "retval") { resolve_args(*probe_); auto it = ap_args_.find("$retval"); if (it != ap_args_.end()) { if (it->second.IsRecordTy()) type_ = it->second.GetName(); else if (it->second.IsPtrTy() && it->second.GetPointeeTy()->IsRecordTy()) type_ = it->second.GetPointeeTy()->GetName(); else type_ = ""; } bpftrace_.btf_set_.insert(type_); } } void FieldAnalyser::visit(Map &map) { MapKey key; if (map.vargs) { for (Expression *expr : *map.vargs) { Visit(*expr); } } auto it = var_types_.find(map.ident); if (it != var_types_.end()) type_ = it->second; } void FieldAnalyser::visit(Variable &var __attribute__((unused))) { auto it = var_types_.find(var.ident); if (it != var_types_.end()) type_ = it->second; } void FieldAnalyser::visit(FieldAccess &acc) { has_builtin_args_ = false; Visit(*acc.expr); if (has_builtin_args_) { auto it = ap_args_.find(acc.field); if (it != ap_args_.end()) { if (it->second.IsRecordTy()) type_ = it->second.GetName(); else if (it->second.IsPtrTy() && it->second.GetPointeeTy()->IsRecordTy()) type_ = it->second.GetPointeeTy()->GetName(); else type_ = ""; } bpftrace_.btf_set_.insert(type_); has_builtin_args_ = false; } else if (!type_.empty()) { type_ = bpftrace_.btf_.type_of(type_, acc.field); bpftrace_.btf_set_.insert(type_); } } void FieldAnalyser::visit(Cast &cast) { Visit(*cast.expr); type_ = cast.cast_type; assert(!type_.empty()); bpftrace_.btf_set_.insert(type_); } void FieldAnalyser::visit(AssignMapStatement &assignment) { Visit(*assignment.map); Visit(*assignment.expr); var_types_.emplace(assignment.map->ident, type_); } void FieldAnalyser::visit(AssignVarStatement &assignment) { Visit(*assignment.expr); var_types_.emplace(assignment.var->ident, type_); } bool FieldAnalyser::compare_args(const ProbeArgs &args1, const ProbeArgs &args2) { auto pred = [](auto a, auto b) { return a.first == b.first; }; return args1.size() == args2.size() && std::equal(args1.begin(), args1.end(), args2.begin(), pred); } bool FieldAnalyser::resolve_args(Probe &probe) { // load AP arguments into ap_args_ ap_args_.clear(); for (auto *ap : *probe.attach_points) { auto probe_type = probetype(ap->provider); if (probe_type != ProbeType::kfunc && probe_type != ProbeType::kretfunc && probe_type != ProbeType::uprobe) continue; // BEGIN and END are special uprobe cases that do not have args if (ap->provider == "BEGIN" || ap->provider == "END") continue; if (ap->need_expansion) { std::set matches; // Find all the matches for the wildcard.. try { matches = bpftrace_.probe_matcher_->get_matches_for_ap(*ap); } catch (const WildcardException &e) { LOG(ERROR) << e.what(); return false; } // ... and check if they share same arguments. bool first = true; for (auto &match : matches) { ProbeArgs args; // Trying to attach to multiple kfuncs. 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::kfunc || probe_type == ProbeType::kretfunc) { try { bpftrace_.btf_.resolve_args(match, first ? ap_args_ : args, probe_type == ProbeType::kretfunc); } catch (const std::runtime_error &e) { LOG(WARNING) << "kfunc:" << ap->func << ": " << e.what(); continue; } } else // uprobe { std::string func = match; std::string file = erase_prefix(func); Dwarf *dwarf = bpftrace_.get_dwarf(file); if (dwarf) { args = dwarf->resolve_args(func); if (first) ap_args_ = args; } else LOG(WARNING, ap->loc, err_) << "No debuginfo found for " << file; } if (!first && !compare_args(args, ap_args_)) { LOG(ERROR, ap->loc, err_) << "Probe has attach points with mixed arguments"; break; } first = false; } } else { // Resolving args for an explicit function failed, print an error and fail if (probe_type == ProbeType::kfunc || probe_type == ProbeType::kretfunc) { try { bpftrace_.btf_.resolve_args(ap->func, ap_args_, probe_type == ProbeType::kretfunc); } catch (const std::runtime_error &e) { LOG(ERROR, ap->loc, err_) << "kfunc:" << ap->func << ": " << e.what(); return false; } } else // uprobe { Dwarf *dwarf = bpftrace_.get_dwarf(ap->target); if (dwarf) ap_args_ = dwarf->resolve_args(ap->func); else { LOG(ERROR, ap->loc, err_) << "No debuginfo found for " << ap->target; } } } // check if we already stored arguments for this probe auto it = bpftrace_.ap_args_.find(probe_->name()); if (it != bpftrace_.ap_args_.end()) { // we did, and it's different...trigger the error if (!compare_args(it->second, ap_args_)) { LOG(ERROR, ap->loc, err_) << "Probe has attach points with mixed arguments"; } } else { // store/save args for each ap for later processing bpftrace_.ap_args_.insert({ probe_->name(), ap_args_ }); } } return true; } 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); } for (Statement *stmt : *probe.stmts) { Visit(*stmt); } } int FieldAnalyser::analyse() { Visit(*root_); std::string errors = err_.str(); if (!errors.empty()) { out_ << errors; return 1; } return 0; } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/field_analyser.h000066400000000000000000000025461413460502400213760ustar00rootroot00000000000000#pragma once #include "bpftrace.h" #include "visitors.h" #include #include #include namespace bpftrace { namespace ast { class FieldAnalyser : public Visitor { public: explicit FieldAnalyser(Node *root, BPFtrace &bpftrace, std::ostream &out = std::cerr) : root_(root), type_(""), bpftrace_(bpftrace), prog_type_(BPF_PROG_TYPE_UNSPEC), out_(out) { } void visit(Identifier &identifier) override; void visit(Builtin &builtin) override; void visit(Map &map) override; void visit(Variable &var) override; void visit(FieldAccess &acc) override; void visit(Cast &cast) override; void visit(AssignMapStatement &assignment) override; void visit(AssignVarStatement &assignment) override; void visit(Probe &probe) override; int analyse(); private: bool resolve_args(Probe &probe); bool compare_args(const ProbeArgs &args1, const ProbeArgs &args2); Node *root_; ProbeType probe_type_; std::string attach_func_; std::string type_; BPFtrace &bpftrace_; bpf_prog_type prog_type_; bool has_builtin_args_; Probe *probe_; std::ostream &out_; std::ostringstream err_; ProbeArgs ap_args_; std::map var_types_; }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/node_counter.h000066400000000000000000000016641413460502400211010ustar00rootroot00000000000000#pragma once #include "bpftrace.h" #include "log.h" #include "pass_manager.h" #include "visitors.h" namespace bpftrace { namespace ast { class NodeCounter : public Visitor { public: void Visit(Node &node) override { count_++; Visitor::Visit(node); } size_t get_count() { return count_; }; private: size_t count_ = 0; }; Pass CreateCounterPass() { auto fn = [](Node &n, PassContext &ctx) { NodeCounter c; c.Visit(n); auto node_count = c.get_count(); auto max = ctx.b.ast_max_nodes_; if (bt_verbose) { LOG(INFO) << "node count: " << node_count; } if (node_count >= max) { LOG(ERROR) << "node count (" << node_count << ") exceeds the limit (" << max << ")"; return PassResult::Error("NodeCounter", "node count exceeded"); } return PassResult::Success(); }; return Pass("NodeCounter", fn); } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/portability_analyser.cpp000066400000000000000000000104701413460502400232030ustar00rootroot00000000000000#include "portability_analyser.h" #include #include "log.h" #include "types.h" namespace bpftrace { namespace ast { PortabilityAnalyser::PortabilityAnalyser(Node *root, std::ostream &out) : root_(root), out_(out) { } int PortabilityAnalyser::analyse() { Visit(*root_); std::string errors = err_.str(); if (!errors.empty()) { out_ << errors; return 1; } return 0; } void PortabilityAnalyser::visit(PositionalParameter ¶m) { // Positional params are only known at runtime. Currently, codegen directly // embeds positional params into the bytecode but that does not work for AOT. // // In theory we could allow positional params for AOT and just embed the // values into the bytecode but there's really no point to that as: // // * that would mislead the user into thinking there's positional param // support // * the user can just hard code the values into their script LOG(ERROR, param.loc, err_) << "AOT does not yet support positional parameters"; } void PortabilityAnalyser::visit(Builtin &builtin) { // `struct task_struct` is unstable across kernel versions and configurations. // This makes it inherently unportable. We must block it until we support // field access relocations. if (builtin.ident == "curtask") { LOG(ERROR, builtin.loc, err_) << "AOT does not yet support accessing `curtask`"; } } void PortabilityAnalyser::visit(Call &call) { if (call.vargs) { for (Expression *expr : *call.vargs) Visit(*expr); } // kaddr() and uaddr() both resolve symbols -> address during codegen and // embeds the values into the bytecode. For AOT to support kaddr()/uaddr(), // the addresses must be resolved at runtime and fixed up during load time. // // cgroupid can vary across systems just like how a process does not // necessarily share the same PID across multiple systems. cgroupid() is also // resolved during codegen and the value embedded into the bytecode. For AOT // to support cgroupid(), the cgroupid must be resolved at runtime and fixed // up during load time. if (call.func == "kaddr" || call.func == "uaddr" || call.func == "cgroupid") { LOG(ERROR, call.loc, err_) << "AOT does not yet support " << call.func << "()"; } } void PortabilityAnalyser::visit(Cast &cast) { Visit(*cast.expr); // The goal here is to block arbitrary field accesses but still allow `args` // access. `args` for tracepoint is fairly stable and should be considered // portable. `args` for k[ret]funcs are type checked by the kernel and may // also be considered stable. For AOT to fully support field accesses, we // need to relocate field access at runtime. LOG(ERROR, cast.loc, err_) << "AOT does not yet support struct casts"; } void PortabilityAnalyser::visit(AttachPoint &ap) { auto type = probetype(ap.provider); // USDT probes require analyzing a USDT enabled binary for precise offsets // and argument information. This analyzing is currently done during codegen // and offsets and type information is embedded into the bytecode. For AOT // support, this analyzing must be done during runtime and fixed up during // load time. if (type == ProbeType::usdt) { LOG(ERROR, ap.loc, err_) << "AOT does not yet support USDT probes"; } // While userspace watchpoint probes are technically portable from codegen // point of view, they require a PID or path via cmdline to resolve address. // watchpoint probes are also API-unstable and need a further change // (see https://github.com/iovisor/bpftrace/issues/1683). // // So disable for now and re-evalulate at another point. else if (type == ProbeType::watchpoint || type == ProbeType::asyncwatchpoint) { LOG(ERROR, ap.loc, err_) << "AOT does not yet support watchpoint probes"; } } Pass CreatePortabilityPass() { auto fn = [](Node &n, PassContext &__attribute__((unused))) { PortabilityAnalyser analyser{ &n }; if (analyser.analyse()) { // Used by runtime test framework to know when to skip an AOT test if (std::getenv("__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED")) std::cout << "__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED" << std::endl; return PassResult::Error(""); } return PassResult::Success(); }; return Pass("PortabilityAnalyser", fn); } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/portability_analyser.h000066400000000000000000000014521413460502400226500ustar00rootroot00000000000000#pragma once #include #include #include "pass_manager.h" #include "visitors.h" namespace bpftrace { namespace ast { // Checks if a script uses any non-portable bpftrace features that AOT // cannot handle. // // Over time, we expect to relax these restrictions as AOT supports more // features. class PortabilityAnalyser : public Visitor { public: PortabilityAnalyser(Node *root, std::ostream &out = std::cerr); int analyse(); private: void visit(PositionalParameter ¶m) override; void visit(Builtin &builtin) override; void visit(Call &call) override; void visit(Cast &cast) override; void visit(AttachPoint &ap) override; Node *root_; std::ostream &out_; std::ostringstream err_; }; Pass CreatePortabilityPass(); } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/printer.cpp000066400000000000000000000164111413460502400204270ustar00rootroot00000000000000#include #include #include #include #include "ast.h" #include "printer.h" namespace bpftrace { namespace ast { void Printer::print(Node *root) { depth_ = 0; Visit(*root); } std::string Printer::type(const SizedType &ty) { std::stringstream buf; if (print_types) { buf << " :: type[" << ty << ", ctx: " << ty.IsCtxAccess(); if (ty.GetAS() != AddrSpace::none) buf << ", AS(" << ty.GetAS() << ")"; buf << "]"; } return buf.str(); } void Printer::visit(Integer &integer) { std::string indent(depth_, ' '); out_ << indent << "int: " << integer.n << type(integer.type) << std::endl; } void Printer::visit(PositionalParameter ¶m) { std::string indent(depth_, ' '); switch (param.ptype) { case PositionalParameterType::positional: out_ << indent << "param: $" << param.n << type(param.type) << std::endl; break; case PositionalParameterType::count: out_ << indent << "param: $#" << type(param.type) << std::endl; break; default: break; } } void Printer::visit(String &string) { std::string indent(depth_, ' '); std::stringstream ss; for (char c: string.str) { // the argument of isprint() must be an unsigned char or EOF int code = static_cast(c); if (std::isprint(code)) { if (c == '\\') ss << "\\\\"; else if (c == '"') ss << "\\\""; else ss << c; } else { if (c == '\n') ss << "\\n"; else if (c == '\t') ss << "\\t"; else if (c == '\r') ss << "\\r"; else ss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << code; } } out_ << indent << "string: " << ss.str() << type(string.type) << std::endl; } void Printer::visit(StackMode &mode) { std::string indent(depth_, ' '); out_ << indent << "stack_mode: " << mode.mode << type(mode.type) << std::endl; } void Printer::visit(Builtin &builtin) { std::string indent(depth_, ' '); out_ << indent << "builtin: " << builtin.ident << type(builtin.type) << std::endl; } void Printer::visit(Identifier &identifier) { std::string indent(depth_, ' '); out_ << indent << "identifier: " << identifier.ident << type(identifier.type) << std::endl; } void Printer::visit(Call &call) { std::string indent(depth_, ' '); out_ << indent << "call: " << call.func << type(call.type) << std::endl; ++depth_; if (call.vargs) { for (Expression *expr : *call.vargs) { expr->accept(*this); } } --depth_; } void Printer::visit(Map &map) { std::string indent(depth_, ' '); out_ << indent << "map: " << map.ident << type(map.type) << std::endl; ++depth_; if (map.vargs) { for (Expression *expr : *map.vargs) { expr->accept(*this); } } --depth_; } void Printer::visit(Variable &var) { std::string indent(depth_, ' '); out_ << indent << "variable: " << var.ident << type(var.type) << std::endl; } void Printer::visit(Binop &binop) { std::string indent(depth_, ' '); out_ << indent << opstr(binop) << type(binop.type) << std::endl; ++depth_; binop.left->accept(*this); binop.right->accept(*this); --depth_; } void Printer::visit(Unop &unop) { if (unop.is_post_op) { std::string indent(depth_ + 1, ' '); unop.expr->accept(*this); out_ << indent << opstr(unop) << std::endl; } else { std::string indent(depth_, ' '); out_ << indent << opstr(unop) << std::endl; ++depth_; unop.expr->accept(*this); --depth_; } } void Printer::visit(Ternary &ternary) { std::string indent(depth_, ' '); out_ << indent << "?:" << std::endl; ++depth_; ternary.cond->accept(*this); ternary.left->accept(*this); ternary.right->accept(*this); --depth_; } void Printer::visit(FieldAccess &acc) { std::string indent(depth_, ' '); out_ << indent << "." << std::endl; ++depth_; acc.expr->accept(*this); --depth_; if (acc.field.size()) out_ << indent << " " << acc.field << std::endl; else out_ << indent << " " << acc.index << std::endl; } void Printer::visit(ArrayAccess &arr) { std::string indent(depth_, ' '); out_ << indent << "[]" << std::endl; ++depth_; arr.expr->accept(*this); arr.indexpr->accept(*this); --depth_; } void Printer::visit(Cast &cast) { std::string indent(depth_, ' '); if (cast.is_pointer) out_ << indent << "(" << cast.cast_type << "*)" << std::endl; else out_ << indent << "(" << cast.cast_type << ")" << std::endl; ++depth_; cast.expr->accept(*this); --depth_; } void Printer::visit(Tuple &tuple) { std::string indent(depth_, ' '); out_ << indent << "tuple:" << std::endl; ++depth_; for (Expression *expr : *tuple.elems) expr->accept(*this); --depth_; } void Printer::visit(ExprStatement &expr) { expr.expr->accept(*this); } void Printer::visit(AssignMapStatement &assignment) { std::string indent(depth_, ' '); out_ << indent << "=" << std::endl; ++depth_; assignment.map->accept(*this); assignment.expr->accept(*this); --depth_; } void Printer::visit(AssignVarStatement &assignment) { std::string indent(depth_, ' '); out_ << indent << "=" << std::endl; ++depth_; assignment.var->accept(*this); assignment.expr->accept(*this); --depth_; } void Printer::visit(If &if_block) { std::string indent(depth_, ' '); out_ << indent << "if" << std::endl; ++depth_; if_block.cond->accept(*this); ++depth_; out_ << indent << " then" << std::endl; for (Statement *stmt : *if_block.stmts) { stmt->accept(*this); } if (if_block.else_stmts) { out_ << indent << " else" << std::endl; for (Statement *stmt : *if_block.else_stmts) { stmt->accept(*this); } } depth_ -= 2; } void Printer::visit(Unroll &unroll) { std::string indent(depth_, ' '); out_ << indent << "unroll" << std::endl; ++depth_; unroll.expr->accept(*this); out_ << indent << " block" << std::endl; ++depth_; for (Statement *stmt : *unroll.stmts) { stmt->accept(*this); } depth_ -= 2; } void Printer::visit(While &while_block) { std::string indent(depth_, ' '); out_ << indent << "while(" << std::endl; ++depth_; while_block.cond->accept(*this); ++depth_; out_ << indent << " )" << std::endl; for (Statement *stmt : *while_block.stmts) { stmt->accept(*this); } } void Printer::visit(Jump &jump) { std::string indent(depth_, ' '); out_ << indent << opstr(jump) << std::endl; } void Printer::visit(Predicate &pred) { std::string indent(depth_, ' '); out_ << indent << "pred" << std::endl; ++depth_; pred.expr->accept(*this); --depth_; } void Printer::visit(AttachPoint &ap) { std::string indent(depth_, ' '); out_ << indent << ap.name(ap.func) << std::endl; } void Printer::visit(Probe &probe) { for (AttachPoint *ap : *probe.attach_points) { ap->accept(*this); } ++depth_; if (probe.pred) { probe.pred->accept(*this); } for (Statement *stmt : *probe.stmts) { stmt->accept(*this); } --depth_; } void Printer::visit(Program &program) { if (program.c_definitions.size() > 0) out_ << program.c_definitions << std::endl; std::string indent(depth_, ' '); out_ << indent << "Program" << std::endl; ++depth_; for (Probe *probe : *program.probes) probe->accept(*this); --depth_; } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/printer.h000066400000000000000000000030111413460502400200640ustar00rootroot00000000000000#pragma once #include "visitors.h" #include namespace bpftrace { namespace ast { class Printer : public Visitor { public: explicit Printer(std::ostream &out, bool print_types = false) : out_(out), print_types(print_types) { } void print(Node *root); void visit(Integer &integer) override; void visit(PositionalParameter ¶m) override; void visit(String &string) override; void visit(StackMode &mode) override; void visit(Identifier &identifier) override; void visit(Builtin &builtin) override; void visit(Call &call) override; void visit(Map &map) override; void visit(Variable &var) override; void visit(Binop &binop) override; void visit(Unop &unop) override; void visit(Ternary &ternary) override; void visit(FieldAccess &acc) override; void visit(ArrayAccess &arr) override; void visit(Cast &cast) override; void visit(Tuple &tuple) override; void visit(ExprStatement &expr) override; void visit(AssignMapStatement &assignment) override; void visit(AssignVarStatement &assignment) override; void visit(If &if_block) override; void visit(Unroll &unroll) override; void visit(While &while_block) override; void visit(Jump &jump) override; void visit(Predicate &pred) override; void visit(AttachPoint &ap) override; void visit(Probe &probe) override; void visit(Program &program) override; int depth_ = 0; private: std::ostream &out_; bool print_types = false; std::string type(const SizedType &ty); }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/resource_analyser.cpp000066400000000000000000000112251413460502400224670ustar00rootroot00000000000000#include "resource_analyser.h" #include "bpftrace.h" #include "struct.h" namespace bpftrace { namespace ast { namespace { // This helper differs from SemanticAnalyser::single_provider_type() in that // for situations where a single probetype is required we assume the AST is // well formed. ProbeType single_provider_type_postsema(Probe *probe) { if (!probe->attach_points->empty()) { return probetype(probe->attach_points->at(0)->provider); } return ProbeType::invalid; } std::string get_literal_string(Expression &expr) { String &str = static_cast(expr); return str.str; } } // namespace ResourceAnalyser::ResourceAnalyser(Node *root) : root_(root), probe_(nullptr) { } RequiredResources ResourceAnalyser::analyse() { Visit(*root_); prepare_seq_printf_ids(); return resources_; } void ResourceAnalyser::visit(Probe &probe) { probe_ = &probe; Visitor::visit(probe); } void ResourceAnalyser::visit(Builtin &builtin) { if (builtin.ident == "elapsed") { resources_.needs_elapsed_map = true; } else if (builtin.ident == "kstack" || builtin.ident == "ustack") { resources_.stackid_maps.insert(StackType{}); } } void ResourceAnalyser::visit(Call &call) { Visitor::visit(call); if (call.func == "printf" || call.func == "system" || call.func == "cat") { std::string fmt = get_literal_string(*call.vargs->at(0)); std::vector args; for (auto it = call.vargs->begin() + 1; it != call.vargs->end(); it++) { // Promote to 64-bit if it's not an aggregate type SizedType ty = (*it)->type; // copy if (!ty.IsAggregate() && !ty.IsTimestampTy()) ty.SetSize(8); args.push_back(Field{ .name = "", .type = ty, .offset = 0, .is_bitfield = false, .bitfield = Bitfield{ .read_bytes = 0, .access_rshift = 0, .mask = 0, }, }); } if (call.func == "printf") { if (single_provider_type_postsema(probe_) == ProbeType::iter) { resources_.seq_printf_args.emplace_back(fmt, args); resources_.needs_data_map = true; } else { resources_.printf_args.emplace_back(fmt, args); } } else if (call.func == "system") { resources_.system_args.emplace_back(fmt, args); } else { resources_.cat_args.emplace_back(fmt, args); } } else if (call.func == "join") { auto delim = call.vargs->size() > 1 ? get_literal_string(*call.vargs->at(1)) : " "; resources_.join_args.push_back(delim); resources_.needs_join_map = true; } else if (call.func == "lhist") { Expression &min_arg = *call.vargs->at(1); Expression &max_arg = *call.vargs->at(2); Expression &step_arg = *call.vargs->at(3); Integer &min = static_cast(min_arg); Integer &max = static_cast(max_arg); Integer &step = static_cast(step_arg); resources_.lhist_args[call.map->ident] = LinearHistogramArgs{ .min = min.n, .max = max.n, .step = step.n, }; } else if (call.func == "time") { if (call.vargs && call.vargs->size() > 0) resources_.time_args.push_back(get_literal_string(*call.vargs->at(0))); else resources_.time_args.push_back("%H:%M:%S\n"); } else if (call.func == "strftime") { resources_.strftime_args.push_back(get_literal_string(*call.vargs->at(0))); } else if (call.func == "print") { auto &arg = *call.vargs->at(0); if (!arg.is_map) resources_.non_map_print_args.push_back(arg.type); } else if (call.func == "kstack" || call.func == "ustack") { resources_.stackid_maps.insert(call.type.stack_type); } } void ResourceAnalyser::visit(Map &map) { Visitor::visit(map); resources_.map_vals[map.ident] = map.type; resources_.map_keys[map.ident] = map.key_type; } void ResourceAnalyser::prepare_seq_printf_ids() { int idx = 0; for (auto &arg : resources_.seq_printf_args) { assert(resources_.needs_data_map); auto len = std::get<0>(arg).size(); resources_.seq_printf_ids.push_back({ idx, len + 1 }); idx += len + 1; } } Pass CreateResourcePass() { auto fn = [](Node &n, PassContext &ctx) { ResourceAnalyser analyser{ &n }; auto resources = analyser.analyse(); ctx.b.resources = resources; // Create fake maps so that codegen has access to map IDs // // At runtime we will replace the fake maps with real maps ctx.b.resources.create_maps(ctx.b, true); return PassResult::Success(); }; return Pass("ResourceAnalyser", fn); } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/resource_analyser.h000066400000000000000000000026551413460502400221430ustar00rootroot00000000000000#pragma once #include #include #include "pass_manager.h" #include "required_resources.h" #include "visitors.h" namespace bpftrace { namespace ast { // Resource analysis pass on AST // // This pass collects information on what runtime resources a script needs. // For example, how many maps to create, what sizes the keys and values are, // all the async printf argument types, etc. // // TODO(danobi): Note that while complete resource collection in this pass is // the goal, there are still places where the goal is not yet realized. For // example the helper error metadata is still being collected during codegen. class ResourceAnalyser : public Visitor { public: ResourceAnalyser(Node *root); // Note we don't return errors here b/c we assume we are run after // semantic analysis and AST is well formed. RequiredResources analyse(); private: void visit(Probe &probe) override; void visit(Builtin &map) override; void visit(Call &call) override; void visit(Map &map) override; // All the seq_printf format strings are stored head to tail in a data // map. This method loads `RequiredResources::seq_printf_ids` with the // starting indicies and lengths of each format string in the data map. void prepare_seq_printf_ids(); RequiredResources resources_; Node *root_; // Current probe we're analysing Probe *probe_; }; Pass CreateResourcePass(); } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/semantic_analyser.cpp000066400000000000000000002736361413460502400224630ustar00rootroot00000000000000#include "semantic_analyser.h" #include "arch/arch.h" #include "ast.h" #include "fake_map.h" #include "log.h" #include "printf.h" #include "probe_matcher.h" #include "signal_bt.h" #include "tracepoint_format_parser.h" #include "usdt.h" #include #include #include #include #include #include namespace bpftrace { namespace ast { 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; } void SemanticAnalyser::visit(Integer &integer) { integer.type = CreateInt64(); } void SemanticAnalyser::visit(PositionalParameter ¶m) { param.type = CreateInt64(); if (func_ == "str") { param.is_in_str = true; has_pos_param_ = true; } switch (param.ptype) { case PositionalParameterType::positional: if (param.n <= 0) LOG(ERROR, param.loc, err_) << "$" << std::to_string(param.n) + " is not a valid parameter"; if (is_final_pass()) { std::string pstr = bpftrace_.get_param(param.n, param.is_in_str); if (!is_numeric(pstr) && !param.is_in_str) { LOG(ERROR, param.loc, err_) << "$" << param.n << " used numerically but given \"" << pstr << "\". Try using str($" << param.n << ")."; } // string allocated in bpf stack. See codegen. if (param.is_in_str) param.type.SetAS(AddrSpace::kernel); } break; case PositionalParameterType::count: if (param.is_in_str) { LOG(ERROR, param.loc, err_) << "use $#, not str($#)"; } break; default: LOG(ERROR, param.loc, err_) << "unknown parameter type"; param.type = CreateNone(); break; } } void SemanticAnalyser::visit(String &string) { // Skip check for printf()'s format string (1st argument) and create the // string with the original size. This is because format string is not part of // bpf byte code. if (func_ == "printf" && func_arg_idx_ == 0) { string.type = CreateString(string.str.size()); return; } if (!is_compile_time_func(func_) && string.str.size() > STRING_SIZE - 1) { LOG(ERROR, string.loc, err_) << "String is too long (over " << STRING_SIZE << " bytes): " << string.str; } string.type = CreateString(STRING_SIZE); // @a = buf("hi", 2). String allocated on bpf stack. See codegen string.type.SetAS(AddrSpace::kernel); } void SemanticAnalyser::visit(StackMode &mode) { mode.type = CreateStackMode(); if (mode.mode == "bpftrace") { mode.type.stack_type.mode = bpftrace::StackMode::bpftrace; } else if (mode.mode == "perf") { mode.type.stack_type.mode = bpftrace::StackMode::perf; } else { mode.type = CreateNone(); LOG(ERROR, mode.loc, err_) << "Unknown stack mode: '" + mode.mode + "'"; } } void SemanticAnalyser::visit(Identifier &identifier) { if (bpftrace_.enums_.count(identifier.ident) != 0) { identifier.type = CreateUInt64(); } else if (bpftrace_.structs.Has(identifier.ident)) { identifier.type = CreateRecord(identifier.ident, bpftrace_.structs.Lookup(identifier.ident)); } else if (func_ == "sizeof" && getIntcasts().count(identifier.ident) != 0) { identifier.type = CreateInt( std::get<0>(getIntcasts().at(identifier.ident))); } else { identifier.type = CreateNone(); LOG(ERROR, identifier.loc, err_) << "Unknown identifier: '" + identifier.ident + "'"; } } void SemanticAnalyser::builtin_args_tracepoint(AttachPoint *attach_point, Builtin &builtin) { /* * tracepoint wildcard expansion, part 2 of 3. This: * 1. expands the wildcard, then sets args to be the first matched probe. * This is so that enough of the type information is available to * survive the later semantic analyser checks. * 2. sets is_tparg so that codegen does the real type setting after * expansion. */ auto matches = bpftrace_.probe_matcher_->get_matches_for_ap(*attach_point); if (!matches.empty()) { auto &match = *matches.begin(); std::string tracepoint_struct = TracepointFormatParser::get_struct_name( match); AddrSpace as = (attach_point->target == "syscalls") ? AddrSpace::user : AddrSpace::kernel; builtin.type = CreatePointer(CreateRecord(tracepoint_struct, bpftrace_.structs.Lookup( tracepoint_struct)), as); builtin.type.MarkCtxAccess(); builtin.type.is_tparg = true; } } ProbeType SemanticAnalyser::single_provider_type(void) { 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::kfunc: case ProbeType::kretfunc: case ProbeType::tracepoint: case ProbeType::iter: 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::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") { ProbeType pt = probetype((*probe_->attach_points)[0]->provider); 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); bpf_prog_type bt2 = progtype(pt); if (bt != bt2) LOG(ERROR, builtin.loc, err_) << "ctx cannot be used in different BPF program types: " << progtypeName(bt) << " and " << progtypeName(bt2); } switch (static_cast(bt)) { case libbpf::BPF_PROG_TYPE_KPROBE: builtin.type = CreatePointer(CreateRecord("struct pt_regs", bpftrace_.structs.Lookup( "structs pt_regs")), AddrSpace::kernel); builtin.type.MarkCtxAccess(); break; case libbpf::BPF_PROG_TYPE_TRACEPOINT: LOG(ERROR, builtin.loc, err_) << "Use args instead of ctx in tracepoint"; break; case libbpf::BPF_PROG_TYPE_PERF_EVENT: builtin.type = CreatePointer( CreateRecord("struct bpf_perf_event_data", bpftrace_.structs.Lookup( "struct bpf_perf_event_data")), AddrSpace::kernel); builtin.type.MarkCtxAccess(); break; case libbpf::BPF_PROG_TYPE_TRACING: if (pt == ProbeType::iter) { std::string type; if (func == "task") { type = "struct bpf_iter__task"; } else if (func == "task_file") { type = "struct bpf_iter__task_file"; } else { LOG(ERROR, builtin.loc, err_) << "unsupported iter type: " << func; } builtin.type = CreatePointer( CreateRecord(type, bpftrace_.structs.Lookup(type)), AddrSpace::kernel); builtin.type.MarkCtxAccess(); } else { LOG(ERROR, builtin.loc, err_) << "invalid program type"; } break; default: LOG(ERROR, builtin.loc, err_) << "invalid program type"; break; } } else if (builtin.ident == "nsecs" || builtin.ident == "elapsed" || builtin.ident == "pid" || builtin.ident == "tid" || builtin.ident == "cgroup" || builtin.ident == "uid" || builtin.ident == "gid" || builtin.ident == "cpu" || builtin.ident == "rand") { builtin.type = CreateUInt64(); if (builtin.ident == "cgroup" && !bpftrace_.feature_->has_helper_get_current_cgroup_id()) { LOG(ERROR, builtin.loc, err_) << "BPF_FUNC_get_current_cgroup_id is not available for your kernel " "version"; } } else if (builtin.ident == "curtask") { /* * Retype curtask to its original type: struct task_struct. */ builtin.type = CreatePointer(CreateRecord("struct task_struct", bpftrace_.structs.Lookup( "struct task_struct")), AddrSpace::kernel); } else if (builtin.ident == "retval") { ProbeType type = single_provider_type(); if (type == ProbeType::kretprobe || type == ProbeType::uretprobe) { builtin.type = CreateUInt64(); } else if (type == ProbeType::kfunc || type == ProbeType::kretfunc) { auto it = ap_args_.find("$retval"); if (it != ap_args_.end()) builtin.type = it->second; else LOG(ERROR, builtin.loc, err_) << "Can't find a field $retval"; } else { LOG(ERROR, builtin.loc, err_) << "The retval builtin can only be used with 'kretprobe' and " << "'uretprobe' and 'kfunc' probes" << (type == ProbeType::tracepoint ? " (try to use args->ret instead)" : ""); } // For kretprobe, kfunc, kretfunc -> AddrSpace::kernel // For uretprobe -> AddrSpace::user builtin.type.SetAS(find_addrspace(type)); } else if (builtin.ident == "kstack") { builtin.type = CreateStack(true, StackType()); } else if (builtin.ident == "ustack") { builtin.type = CreateStack(false, StackType()); } else if (builtin.ident == "comm") { builtin.type = CreateString(COMM_SIZE); // comm allocated in the bpf stack. See codegen // Case: @=comm and strncmp(@, "name") builtin.type.SetAS(AddrSpace::kernel); } else if (builtin.ident == "func") { for (auto &attach_point : *probe_->attach_points) { ProbeType type = probetype(attach_point->provider); if (type == ProbeType::kprobe || type == ProbeType::kretprobe) builtin.type = CreateKSym(); else if (type == ProbeType::uprobe || type == ProbeType::uretprobe) builtin.type = CreateUSym(); else LOG(ERROR, builtin.loc, err_) << "The func builtin can not be used with '" << attach_point->provider << "' probes"; } } else if (!builtin.ident.compare(0, 3, "arg") && builtin.ident.size() == 4 && builtin.ident.at(3) >= '0' && builtin.ident.at(3) <= '9') { 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 && type != ProbeType::usdt) LOG(ERROR, builtin.loc, err_) << "The " << builtin.ident << " builtin can only be used with " << "'kprobes', 'uprobes' and 'usdt' probes"; } int arg_num = atoi(builtin.ident.substr(3).c_str()); if (arg_num > arch::max_arg()) LOG(ERROR, builtin.loc, err_) << arch::name() << " doesn't support " << builtin.ident; builtin.type = CreateUInt64(); builtin.type.SetAS(addrspace); } else if (!builtin.ident.compare(0, 4, "sarg") && builtin.ident.size() == 5 && builtin.ident.at(4) >= '0' && builtin.ident.at(4) <= '9') { ProbeType pt = probetype((*probe_->attach_points)[0]->provider); AddrSpace addrspace = find_addrspace(pt); for (auto &attach_point : *probe_->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::kprobe && type != ProbeType::uprobe) LOG(ERROR, builtin.loc, err_) << "The " + builtin.ident << " builtin can only be used with 'kprobes' and 'uprobes' probes"; if (is_final_pass() && (attach_point->address != 0 || attach_point->func_offset != 0)) { // If sargX values are needed when using an offset, they can be stored in a map // when entering the function and then referenced from an offset-based probe LOG(WARNING, builtin.loc, out_) << "Using an address offset with the sargX built-in can" "lead to unexpected behavior "; } } builtin.type = CreateUInt64(); builtin.type.SetAS(addrspace); } else if (builtin.ident == "probe") { builtin.type = CreateProbe(); probe_->need_expansion = true; } else if (builtin.ident == "username") { builtin.type = CreateUsername(); } else if (builtin.ident == "cpid") { if (!has_child_) { LOG(ERROR, builtin.loc, err_) << "cpid cannot be used without child command"; } builtin.type = CreateUInt32(); } else if (builtin.ident == "args") { for (auto &attach_point : *probe_->attach_points) { ProbeType type = probetype(attach_point->provider); if (type == ProbeType::tracepoint) { probe_->need_expansion = true; builtin_args_tracepoint(attach_point, builtin); } } ProbeType type = single_provider_type(); if (type == ProbeType::tracepoint) { // no special action in here } else if (type == ProbeType::kfunc || type == ProbeType::kretfunc) { builtin.type = CreatePointer(CreateRecord("struct kfunc", bpftrace_.structs.Lookup( "struct kfunc")), AddrSpace::kernel); builtin.type.MarkCtxAccess(); builtin.type.is_funcarg = true; } else if (type == ProbeType::uprobe) { // Add a dummy "placeholder" type here to satisfy semantic analyser. // An eventual FieldAccess is then resolved as an argument lookup. builtin.type = CreatePointer(CreateRecord("struct uprobe_args", bpftrace_.structs.Lookup( "struct uprobe_args")), AddrSpace::user); builtin.type.MarkCtxAccess(); builtin.type.is_funcarg = true; } else { LOG(ERROR, builtin.loc, err_) << "The args builtin can only be used with tracepoint/kfunc/uprobe" << "probes (" << probetypeName(type) << " used here)"; } } else { builtin.type = CreateNone(); LOG(ERROR, builtin.loc, err_) << "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)) { LOG(ERROR, call.loc, err_) << call.func << "() is an unsafe function being used in safe mode"; } struct func_setter { func_setter(SemanticAnalyser &analyser, const std::string &s) : analyser_(analyser), old_func_(analyser_.func_) { analyser_.func_ = s; } ~func_setter() { analyser_.func_ = old_func_; analyser_.func_arg_idx_ = -1; } private: SemanticAnalyser &analyser_; std::string old_func_; }; func_setter scope_bound_func_setter{ *this, call.func }; if (call.vargs) { for (size_t i = 0; i < call.vargs->size(); ++i) { auto &expr = (*call.vargs)[i]; func_arg_idx_ = i; expr->accept(*this); } } for (auto &ap : *probe_->attach_points) { if (!check_available(call, *ap)) { LOG(ERROR, call.loc, err_) << call.func << " can not be used with \"" << ap->provider << "\" probes"; } } if (call.func == "hist") { check_assignment(call, true, false, false); check_nargs(call, 1); check_arg(call, Type::integer, 0); call.type = CreateHist(); } else if (call.func == "lhist") { check_assignment(call, true, false, false); if (check_nargs(call, 4)) { check_arg(call, Type::integer, 0, false); check_arg(call, Type::integer, 1, true); check_arg(call, Type::integer, 2, true); check_arg(call, Type::integer, 3, true); } if (is_final_pass()) { Expression *min_arg = call.vargs->at(1); Expression *max_arg = call.vargs->at(2); Expression *step_arg = call.vargs->at(3); auto min = bpftrace_.get_int_literal(min_arg); auto max = bpftrace_.get_int_literal(max_arg); auto step = bpftrace_.get_int_literal(step_arg); if (!min.has_value()) { LOG(ERROR, call.loc, err_) << call.func << ": invalid min value"; return; } if (!max.has_value()) { LOG(ERROR, call.loc, err_) << call.func << ": invalid max value"; return; } if (!step.has_value()) { LOG(ERROR, call.loc, err_) << call.func << ": invalid step value"; return; } if (*step <= 0) { LOG(ERROR, call.loc, err_) << "lhist() step must be >= 1 (" << *step << " provided)"; } else { int buckets = (*max - *min) / *step; if (buckets > 1000) { LOG(ERROR, call.loc, err_) << "lhist() too many buckets, must be <= 1000 (would need " << buckets << ")"; } } if (*min < 0) { LOG(ERROR, call.loc, err_) << "lhist() min must be non-negative (provided min " << *min << ")"; } if (*min > *max) { LOG(ERROR, call.loc, err_) << "lhist() min must be less than max (provided min " << *min << " and max " << *max << ")"; } if ((*max - *min) < *step) { LOG(ERROR, call.loc, err_) << "lhist() step is too large for the given range (provided step " << *step << " for range " << (*max - *min) << ")"; } } call.type = CreateLhist(); } else if (call.func == "count") { check_assignment(call, true, false, false); check_nargs(call, 0); call.type = CreateCount(true); } else if (call.func == "sum") { bool sign = false; check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); sign = call.vargs->at(0)->type.IsSigned(); } call.type = CreateSum(sign); } else if (call.func == "min") { bool sign = false; check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); sign = call.vargs->at(0)->type.IsSigned(); } call.type = CreateMin(sign); } else if (call.func == "max") { bool sign = false; check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); sign = call.vargs->at(0)->type.IsSigned(); } call.type = CreateMax(sign); } else if (call.func == "avg") { check_assignment(call, true, false, false); check_nargs(call, 1); check_arg(call, Type::integer, 0); call.type = CreateAvg(true); } else if (call.func == "stats") { check_assignment(call, true, false, false); check_nargs(call, 1); check_arg(call, Type::integer, 0); call.type = CreateStats(true); } else if (call.func == "delete") { check_assignment(call, false, false, false); if (check_nargs(call, 1)) { auto &arg = *call.vargs->at(0); if (!arg.is_map) LOG(ERROR, call.loc, err_) << "delete() expects a map to be provided"; } call.type = CreateNone(); } else if (call.func == "str") { if (check_varargs(call, 1, 2)) { auto *arg = call.vargs->at(0); auto &t = arg->type; if (!t.IsIntegerTy() && !t.IsPtrTy()) { LOG(ERROR, call.loc, err_) << call.func << "() expects an integer or a pointer type as first " << "argument (" << t << " provided)"; } call.type = CreateString(bpftrace_.strlen_); if (has_pos_param_) { if (dynamic_cast(arg)) call.is_literal = true; else { auto binop = dynamic_cast(arg); if (!(binop && (dynamic_cast(binop->left) || dynamic_cast(binop->right)))) { // Only str($1), str($1 + CONST), or str(CONST + $1) are allowed LOG(ERROR, call.loc, err_) << call.func << "() only accepts positional parameters" << " directly or with a single constant offset added"; } } } if (is_final_pass() && call.vargs->size() == 2 && check_arg(call, Type::integer, 1, false)) { auto &size_arg = *call.vargs->at(1); if (size_arg.is_literal) { auto &integer = static_cast(size_arg); long value = integer.n; if (value < 0) LOG(ERROR, call.loc, err_) << call.func << "cannot use negative length (" << value << ")"; } } // Required for cases like strncmp(str($1), str(2), 4)) call.type.SetAS(t.GetAS()); } has_pos_param_ = false; } else if (call.func == "buf") { if (!check_varargs(call, 1, 2)) return; auto &arg = *call.vargs->at(0); if (is_final_pass() && !(arg.type.IsIntTy() || arg.type.IsStringTy() || arg.type.IsPtrTy() || arg.type.IsArrayTy())) { LOG(ERROR, call.loc, err_) << call.func << "() expects an integer, string, or array argument but saw " << typestr(arg.type.type); } size_t max_buffer_size = bpftrace_.strlen_; size_t buffer_size = max_buffer_size; if (call.vargs->size() == 1) { if (arg.type.IsArrayTy()) buffer_size = arg.type.GetNumElements() * arg.type.GetElementTy()->GetSize(); else if (is_final_pass()) LOG(ERROR, call.loc, err_) << call.func << "() expects a length argument for non-array type " << typestr(arg.type.type); } else { if (is_final_pass()) check_arg(call, Type::integer, 1, false); auto &size_arg = *call.vargs->at(1); if (size_arg.type.IsIntTy() && size_arg.is_literal) { auto value = bpftrace_.get_int_literal(&size_arg); if (value.has_value()) { if (*value < 0) { LOG(ERROR, call.loc, err_) << call.func << " cannot use negative length (" << *value << ")"; } buffer_size = *value; } else LOG(ERROR, call.loc, err_) << call.func << ": invalid length value"; } } if (buffer_size > max_buffer_size) { if (is_final_pass()) LOG(WARNING, call.loc, out_) << call.func << "() length is too long and will be shortened to " << std::to_string(bpftrace_.strlen_) << " bytes (see BPFTRACE_STRLEN)"; buffer_size = max_buffer_size; } buffer_size++; // extra byte is used to embed the length of the buffer call.type = CreateBuffer(buffer_size); // Consider case : $a = buf("hi", 2); $b = buf("bye", 3); $a == $b // The result of buf is copied to bpf stack. Hence kernel probe read call.type.SetAS(AddrSpace::kernel); } else if (call.func == "ksym" || call.func == "usym") { if (check_nargs(call, 1)) { // allow symbol lookups on casts (eg, function pointers) auto &arg = *call.vargs->at(0); auto &type = arg.type; if (!type.IsIntegerTy() && !type.IsPtrTy()) LOG(ERROR, call.loc, err_) << call.func << "() expects an integer or pointer argument"; } if (call.func == "ksym") call.type = CreateKSym(); else if (call.func == "usym") call.type = CreateUSym(); } else if (call.func == "ntop") { if (!check_varargs(call, 1, 2)) return; auto arg = call.vargs->at(0); if (call.vargs->size() == 2) { arg = call.vargs->at(1); check_arg(call, Type::integer, 0); } if (!arg->type.IsIntTy() && !arg->type.IsStringTy() && !arg->type.IsArrayTy()) LOG(ERROR, call.loc, err_) << call.func << "() expects an integer or array argument, got " << arg->type.type; // Kind of: // // struct { // int af_type; // union { // char[4] inet4; // char[16] inet6; // } // } int buffer_size = 24; auto type = arg->type; if ((arg->type.IsArrayTy() || arg->type.IsStringTy()) && type.GetSize() != 4 && type.GetSize() != 16) LOG(ERROR, call.loc, err_) << call.func << "() argument must be 4 or 16 bytes in size"; call.type = CreateInet(buffer_size); } else if (call.func == "join") { check_assignment(call, false, false, false); call.type = CreateNone(); if (!check_varargs(call, 1, 2)) return; if (!is_final_pass()) return; auto &arg = *call.vargs->at(0); if (!(arg.type.IsIntTy() || arg.type.IsPtrTy())) { LOG(ERROR, call.loc, err_) << "() only supports int or pointer arguments" << " (" << arg.type.type << " provided)"; } if (call.vargs && call.vargs->size() > 1) check_arg(call, Type::string, 1, true); } else if (call.func == "reg") { if (check_nargs(call, 1)) { if (check_arg(call, Type::string, 0, true)) { auto reg_name = bpftrace_.get_string_literal(call.vargs->at(0)); int offset = arch::offset(reg_name);; if (offset == -1) { LOG(ERROR, call.loc, err_) << "'" << reg_name << "' is not a valid register on this architecture" << " (" << arch::name() << ")"; } } } call.type = CreateUInt64(); ProbeType pt = single_provider_type(); // In case of different attach_points, Set the addrspace to none. call.type.SetAS(find_addrspace(pt)); } else if (call.func == "kaddr") { if (check_nargs(call, 1)) { check_arg(call, Type::string, 0, true); } call.type = CreateUInt64(); call.type.SetAS(AddrSpace::kernel); } else if (call.func == "uaddr") { if (!check_nargs(call, 1)) return; if (!(check_arg(call, Type::string, 0, true) && check_symbol(call, 0))) return; std::vector sizes; auto name = bpftrace_.get_string_literal(call.vargs->at(0)); for (auto &ap : *probe_->attach_points) { struct symbol sym = {}; int err = bpftrace_.resolve_uname(name, &sym, ap->target); if (err < 0 || sym.address == 0) { LOG(ERROR, call.loc, err_) << "Could not resolve symbol: " << ap->target << ":" << name; } sizes.push_back(sym.size); } for (size_t i = 1; i < sizes.size(); i++) { if (sizes.at(0) != sizes.at(i)) { LOG(ERROR, call.loc, err_) << "Symbol size mismatch between probes. Symbol \"" << name << "\" has size " << sizes.at(0) << " for probe \"" << probe_->attach_points->at(0)->name("") << "\" but size " << sizes.at(i) << " for probe \"" << probe_->attach_points->at(i)->name("") << "\""; } } size_t pointee_size = 0; switch (sizes.at(0)) { case 1: case 2: case 4: pointee_size = sizes.at(0) * 8; break; default: pointee_size = 64; } call.type = CreatePointer(CreateInt(pointee_size), AddrSpace::user); } else if (call.func == "cgroupid") { if (check_nargs(call, 1)) { check_arg(call, Type::string, 0, true); } call.type = CreateUInt64(); } else if (call.func == "printf" || call.func == "system" || call.func == "cat") { check_assignment(call, false, false, false); if (check_varargs(call, 1, 128)) { check_arg(call, Type::string, 0, true); if (is_final_pass()) { auto &fmt_arg = *call.vargs->at(0); String &fmt = static_cast(fmt_arg); std::vector args; for (auto iter = call.vargs->begin() + 1; iter != call.vargs->end(); iter++) { // NOTE: modifying the type will break the resizing that happens // in the codegen. We have to copy the type here to avoid modification SizedType ty = (*iter)->type; // Promote to 64-bit if it's not an aggregate type if (!ty.IsAggregate() && !ty.IsTimestampTy()) ty.SetSize(8); args.push_back(Field{ .name = "", .type = ty, .offset = 0, .is_bitfield = false, .bitfield = Bitfield{ .read_bytes = 0, .access_rshift = 0, .mask = 0, }, }); } std::string msg = verify_format_string(fmt.str, args); if (msg != "") { LOG(ERROR, call.loc, err_) << msg; } } } call.type = CreateNone(); } else if (call.func == "exit") { check_assignment(call, false, false, false); check_nargs(call, 0); } else if (call.func == "print") { check_assignment(call, false, false, false); if (in_loop() && is_final_pass()) { LOG(WARNING, call.loc, out_) << "Due to it's asynchronous nature using 'print()' in a loop can " "lead to unexpected behavior. The map will likely be updated " "before the runtime can 'print' it."; } if (check_varargs(call, 1, 3)) { auto &arg = *call.vargs->at(0); if (arg.is_map) { Map &map = static_cast(arg); map.skip_key_validation = true; if (map.vargs != nullptr) { LOG(ERROR, call.loc, err_) << "The map passed to " << call.func << "() should not be " << "indexed by a key"; } if (is_final_pass()) { if (call.vargs->size() > 1) check_arg(call, Type::integer, 1, true); if (call.vargs->size() > 2) check_arg(call, Type::integer, 2, true); if (map.type.IsStatsTy() && call.vargs->size() > 1) { LOG(WARNING, call.loc, out_) << "print()'s top and div arguments are ignored when used on " "stats() maps."; } } } // Note that IsPrintableTy() is somewhat disingenuous here. Printing a // non-map value requires being able to serialize the entire value, so // map-backed types like count(), min(), max(), etc. cannot be printed // through the non-map printing mechanism. // // We rely on the fact that semantic analysis enforces types like count(), // min(), max(), etc. to be assigned directly to a map. This ensures that // the previous `arg.is_map` arm is hit first. else if (arg.type.IsPrintableTy()) { if (call.vargs->size() != 1) LOG(ERROR, call.loc, err_) << "Non-map print() only takes 1 argument, " << call.vargs->size() << " found"; } else { if (is_final_pass()) LOG(ERROR, call.loc, err_) << arg.type << " type passed to " << call.func << "() is not printable"; } } } else if (call.func == "clear") { check_assignment(call, false, false, false); if (check_nargs(call, 1)) { auto &arg = *call.vargs->at(0); if (!arg.is_map) LOG(ERROR, call.loc, err_) << "clear() expects a map to be provided"; else { Map &map = static_cast(arg); map.skip_key_validation = true; if (map.vargs != nullptr) { LOG(ERROR, call.loc, err_) << "The map passed to " << call.func << "() should not be " << "indexed by a key"; } } } } else if (call.func == "zero") { check_assignment(call, false, false, false); if (check_nargs(call, 1)) { auto &arg = *call.vargs->at(0); if (!arg.is_map) LOG(ERROR, call.loc, err_) << "zero() expects a map to be provided"; else { Map &map = static_cast(arg); map.skip_key_validation = true; if (map.vargs != nullptr) { LOG(ERROR, call.loc, err_) << "The map passed to " << call.func << "() should not be " << "indexed by a key"; } } } } else if (call.func == "time") { check_assignment(call, false, false, false); if (check_varargs(call, 0, 1)) { if (is_final_pass()) { if (call.vargs && call.vargs->size() > 0) check_arg(call, Type::string, 0, true); } } } else if (call.func == "strftime") { call.type = CreateTimestamp(); check_varargs(call, 2, 2) && is_final_pass() && check_arg(call, Type::string, 0, true) && check_arg(call, Type::integer, 1, false); } else if (call.func == "kstack") { check_stack_call(call, true); } else if (call.func == "ustack") { check_stack_call(call, false); } else if (call.func == "signal") { if (!bpftrace_.feature_->has_helper_send_signal()) { LOG(ERROR, call.loc, err_) << "BPF_FUNC_send_signal not available for your kernel version"; } check_assignment(call, false, false, false); if (!check_varargs(call, 1, 1)) { return; } auto &arg = *call.vargs->at(0); if (arg.type.IsStringTy() && arg.is_literal) { auto sig = bpftrace_.get_string_literal(&arg); if (signal_name_to_num(sig) < 1) { LOG(ERROR, call.loc, err_) << sig << " is not a valid signal"; } } else if (arg.type.IsIntTy() && arg.is_literal) { auto sig = bpftrace_.get_int_literal(&arg); if (!sig.has_value() || *sig < 1 || *sig > 64) { LOG(ERROR, call.loc, err_) << std::to_string(*sig) << " is not a valid signal, allowed range: [1,64]"; } } else if(arg.type.type != Type::integer) { LOG(ERROR, call.loc, err_) << "signal only accepts string literals or integers"; } } else if (call.func == "sizeof") { // sizeof() is a interesting builtin because the arguments can be either // an expression or a type. As a result, the only thing we'll check here // is that we have a single argument. check_nargs(call, 1); call.type = CreateUInt64(); } else if (call.func == "path") { if (!bpftrace_.feature_->has_d_path()) { LOG(ERROR, call.loc, err_) << "BPF_FUNC_d_path not available for your kernel version"; } if (check_varargs(call, 1, 1)) { // Argument for path can be both record and pointer. // It's pointer when it's passed directly from the probe // argument, like: path(args->path)) // It's record when it's referenced as object pointer // member, like: path(args->filp->f_path)) if (!check_arg(call, Type::record, 0, false, false) && !check_arg(call, Type::pointer, 0, false, false)) { auto &arg = *call.vargs->at(0); LOG(ERROR, call.loc, err_) << "path() only supports pointer or record argument (" << arg.type.type << " provided)"; } call.type = SizedType(Type::string, bpftrace_.strlen_); } for (auto &attach_point : *probe_->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::kfunc && type != ProbeType::kretfunc && type != ProbeType::iter) LOG(ERROR, call.loc, err_) << "The path function can only be used with " << "'kfunc', 'kretfunc', 'iter' probes"; } } else if (call.func == "strncmp") { if (check_nargs(call, 3)) { check_arg(call, Type::string, 0); check_arg(call, Type::string, 1); if (check_arg(call, Type::integer, 2, true)){ auto size = bpftrace_.get_int_literal(call.vargs->at(2)); if (size.has_value()) { if (size < 0) LOG(ERROR, call.loc, err_) << "Builtin strncmp requires a non-negative size"; } else LOG(ERROR, call.loc, err_) << call.func << ": invalid size value"; } } call.type = CreateUInt64(); } else if (call.func == "override") { if (!bpftrace_.feature_->has_helper_override_return()) { LOG(ERROR, call.loc, err_) << "BPF_FUNC_override_return not available for your kernel version"; } check_assignment(call, false, false, false); if (check_varargs(call, 1, 1)) { check_arg(call, Type::integer, 0, false); } for (auto &attach_point : *probe_->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::kprobe) { LOG(ERROR, call.loc, err_) << call.func << " can only be used with kprobes."; } } } else if (call.func == "kptr" || call.func == "uptr") { if (!check_nargs(call, 1)) return; // kptr should accept both integer or pointer. Consider case: kptr($1) auto &arg = *call.vargs->at(0); if (arg.type.type != Type::integer && arg.type.type != Type::pointer) { LOG(ERROR, call.loc, err_) << call.func << "() only supports " << "integer or pointer arguments (" << arg.type.type << " provided)"; return; } auto as = (call.func == "kptr" ? AddrSpace::kernel : AddrSpace::user); call.type = call.vargs->front()->type; call.type.SetAS(as); } else if (call.func == "macaddr") { if (!check_nargs(call, 1)) return; auto &arg = call.vargs->at(0); if (!arg->type.IsIntTy() && !arg->type.IsArrayTy() && !arg->type.IsByteArray() && !arg->type.IsPtrTy()) LOG(ERROR, call.loc, err_) << call.func << "() only supports array or pointer arguments" << " (" << arg->type.type << " provided)"; auto type = arg->type; if ((type.IsArrayTy() || type.IsByteArray()) && type.GetSize() != 6) LOG(ERROR, call.loc, err_) << call.func << "() argument must be 6 bytes in size"; call.type = CreateMacAddress(); } else if (call.func == "unwatch") { if (check_nargs(call, 1)) check_arg(call, Type::integer, 0); // Return type cannot be used call.type = SizedType(Type::none, 0); } else { LOG(ERROR, call.loc, err_) << "Unknown function: '" << call.func << "'"; call.type = CreateNone(); } } void SemanticAnalyser::check_stack_call(Call &call, bool kernel) { call.type = CreateStack(kernel); if (!check_varargs(call, 0, 2)) { return; } StackType stack_type; if (call.vargs) { switch (call.vargs->size()) { case 0: break; case 1: { auto &arg = *call.vargs->at(0); // If we have a single argument it can be either // stack-mode or stack-size if (arg.type.IsStackModeTy()) { if (check_arg(call, Type::stack_mode, 0, true)) stack_type.mode = static_cast(arg).type.stack_type.mode; } else { if (check_arg(call, Type::integer, 0, true)) { auto limit = bpftrace_.get_int_literal(&arg); if (limit.has_value()) stack_type.limit = *limit; else LOG(ERROR, call.loc, err_) << call.func << ": invalid limit value"; } } break; } case 2: { if (check_arg(call, Type::stack_mode, 0, true)) { auto &mode_arg = *call.vargs->at(0); stack_type.mode = static_cast(mode_arg).type.stack_type.mode; } if (check_arg(call, Type::integer, 1, true)) { auto &limit_arg = call.vargs->at(1); auto limit = bpftrace_.get_int_literal(limit_arg); if (limit.has_value()) stack_type.limit = *limit; else LOG(ERROR, call.loc, err_) << call.func << ": invalid limit value"; } break; } default: LOG(ERROR, call.loc, err_) << "Invalid number of arguments"; break; } } if (stack_type.limit > MAX_STACK_SIZE) { LOG(ERROR, call.loc, err_) << call.func << "([int limit]): limit shouldn't exceed " << MAX_STACK_SIZE << ", " << stack_type.limit << " given"; } call.type = CreateStack(kernel, stack_type); } void SemanticAnalyser::visit(Map &map) { MapKey key; if (map.vargs) { for (unsigned int i = 0; i < map.vargs->size(); i++){ Expression * expr = map.vargs->at(i); expr->accept(*this); // Insert a cast to 64 bits if needed by injecting // a cast into the ast. if (expr->type.IsIntTy() && expr->type.GetSize() < 8) { std::string type = expr->type.IsSigned() ? "int64" : "uint64"; Expression *cast = new ast::Cast(type, false, false, expr, map.loc); cast->accept(*this); map.vargs->at(i) = cast; expr = cast; } else if (expr->type.IsCtxAccess()) { // map functions only accepts a pointer to a element in the stack LOG(ERROR, map.loc, err_) << "context cannot be used as a map key"; } else if (expr->type.type == Type::tuple) { LOG(ERROR, map.loc, err_) << "tuple cannot be used as a map key. Try a multi-key associative" " array instead (eg `@map[$1, $2] = ...)`."; } if (is_final_pass()) { if (expr->type.IsNoneTy()) LOG(ERROR, expr->loc, err_) << "Invalid expression for assignment: "; SizedType keytype = expr->type; // Skip.IsSigned() when comparing keys to not break existing scripts // which use maps as a lookup table // TODO (fbs): This needs a better solution if (expr->type.IsIntTy()) keytype = CreateUInt(keytype.GetSize() * 8); key.args_.push_back(keytype); } } } if (is_final_pass()) { if (!map.skip_key_validation) { auto search = map_key_.find(map.ident); if (search != map_key_.end()) { if (search->second != key) { LOG(ERROR, map.loc, err_) << "Argument mismatch for " << map.ident << ": " << "trying to access with arguments: " << key.argument_type_list() << " when map expects arguments: " << search->second.argument_type_list(); } } else { map_key_.insert({map.ident, key}); } } } auto search_val = map_val_.find(map.ident); if (search_val != map_val_.end()) { map.type = search_val->second; } else { if (is_final_pass()) { LOG(ERROR, map.loc, err_) << "Undefined map: " << map.ident; } map.type = CreateNone(); } // MapKey default initializes to no args so we don't need to do anything // if we don't find a key here auto map_key_search_val = map_key_.find(map.ident); if (map_key_search_val != map_key_.end()) map.key_type = map_key_search_val->second; } void SemanticAnalyser::visit(Variable &var) { auto search_val = variable_val_.find(var.ident); if (search_val != variable_val_.end()) { var.type = search_val->second; } else { LOG(ERROR, var.loc, err_) << "Undefined or undeclared variable: " << var.ident; var.type = CreateNone(); } } void SemanticAnalyser::visit(ArrayAccess &arr) { arr.expr->accept(*this); arr.indexpr->accept(*this); SizedType &type = arr.expr->type; SizedType &indextype = arr.indexpr->type; if (is_final_pass()) { if (!type.IsArrayTy() && !type.IsPtrTy()) { LOG(ERROR, arr.loc, err_) << "The array index operator [] can only be " "used on arrays and pointers, found " << type.type << "."; return; } if (type.IsPtrTy() && type.GetPointeeTy()->GetSize() == 0) { LOG(ERROR, arr.loc, err_) << "The array index operator [] cannot be used " "on a pointer to an unsized type (void *)."; } if (indextype.IsIntTy() && arr.indexpr->is_literal) { if (type.IsArrayTy()) { auto index = bpftrace_.get_int_literal(arr.indexpr); if (index.has_value()) { if ((size_t)*index >= type.GetNumElements()) LOG(ERROR, arr.loc, err_) << "the index " << *index << " is out of bounds for array of size " << type.GetNumElements(); } else LOG(ERROR, arr.loc, err_) << "invalid index expression"; } } else { LOG(ERROR, arr.loc, err_) << "The array index operator [] only " "accepts literal integer indices."; } } if (type.IsArrayTy()) arr.type = *type.GetElementTy(); else if (type.IsPtrTy()) arr.type = *type.GetPointeeTy(); else arr.type = CreateNone(); arr.type.is_internal = type.is_internal; arr.type.SetAS(type.GetAS()); } 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; // 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->is_literal && *bpftrace_.get_int_literal(left) >= 0) { lsign = false; } // The reverse (10 < a) should also hold else if (!lsign && rsign && right->is_literal && *bpftrace_.get_int_literal(right) >= 0) { rsign = false; } else { switch (binop.op) { case Operator::EQ: case Operator::NE: case Operator::LE: case Operator::GE: case Operator::LT: case Operator::GT: LOG(WARNING, binop.loc, out_) << "comparison of integers of different signs: '" << left->type << "' and '" << right->type << "'" << " can lead to undefined behavior"; break; case Operator::PLUS: case Operator::MINUS: case Operator::MUL: case Operator::DIV: case Operator::MOD: LOG(WARNING, binop.loc, out_) << "arithmetic on integers of different signs: '" << left->type << "' and '" << right->type << "'" << " can lead to undefined behavior"; break; default: break; } } } // Next, warn on any operations that require signed division. // // SDIV is not implemented for bpf. See Documentation/bpf/bpf_design_QA // in kernel sources if (binop.op == Operator::DIV || binop.op == Operator::MOD) { // Convert operands to unsigned if possible if (lsign && left->is_literal && *bpftrace_.get_int_literal(left) >= 0) lsign = false; if (rsign && right->is_literal && *bpftrace_.get_int_literal(right) >= 0) rsign = false; // If they're still signed, we have to warn if (lsign || rsign) { LOG(WARNING, binop.loc, out_) << "signed operands for '" << opstr(binop) << "' can lead to undefined behavior " << "(cast to unsigned to silence warning)"; } } if (func_ == "str") { // Check if one of the operands is a positional parameter // The other one should be a constant offset auto pos_param = dynamic_cast(left); auto offset = dynamic_cast(right); if (!pos_param) { pos_param = dynamic_cast(right); offset = dynamic_cast(left); } if (pos_param) { auto len = bpftrace_.get_param(pos_param->n, true).length(); if (!offset || binop.op != Operator::PLUS || offset->n < 0 || (size_t)offset->n > len) { LOG(ERROR, binop.loc + binop.right->loc, err_) << "only addition of a single constant less or equal to the " << "length of $" << pos_param->n << " (which is " << len << ")" << " is allowed inside str()"; } } } } void SemanticAnalyser::binop_ptr(Binop &binop) { auto &lht = binop.left->type; auto &rht = binop.right->type; bool left_is_ptr = lht.IsPtrTy(); auto &ptr = left_is_ptr ? lht : rht; auto &other = left_is_ptr ? rht : lht; auto compare = 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; default:; } auto invalid_op = [&binop, this, &lht, &rht]() { LOG(ERROR, binop.loc, err_) << "The " << opstr(binop) << " operator can not be used on expressions of types " << lht << ", " << rht; }; // Binop on two pointers if (other.IsPtrTy()) { if (compare) { binop.type = CreateUInt(64); if (is_final_pass()) { auto le = lht.GetPointeeTy(); auto re = rht.GetPointeeTy(); if (*le != *re) { LOG(WARNING, binop.left->loc + binop.right->loc, out_) << "comparison of distinct pointer types ('" << *le << ", '" << *re << "')"; } } } else invalid_op(); } // Binop on a pointer and int else if (other.IsIntTy()) { // sum is associative but minus only works with pointer on the left hand // side if (binop.op == Operator::MINUS && !left_is_ptr) invalid_op(); else if (binop.op == Operator::PLUS || binop.op == Operator::MINUS) binop.type = CreatePointer(*ptr.GetPointeeTy(), ptr.GetAS()); else if (compare) binop.type = CreateInt(64); else invalid_op(); } // Binop on a pointer and something else else { invalid_op(); } } void SemanticAnalyser::visit(Binop &binop) { binop.left->accept(*this); binop.right->accept(*this); auto &lht = binop.left->type; auto &rht = binop.right->type; bool lsign = binop.left->type.IsSigned(); bool rsign = binop.right->type.IsSigned(); if (lht.IsPtrTy() || rht.IsPtrTy()) { binop_ptr(binop); return; } bool is_signed = lsign && rsign; switch (binop.op) { case Operator::LEFT: case Operator::RIGHT: is_signed = lsign; break; default: break; } binop.type = CreateInteger(64, is_signed); auto addr_lhs = binop.left->type.GetAS(); auto addr_rhs = binop.right->type.GetAS(); // if lhs or rhs has different addrspace (not none), then set the // addrspace to none. This preserves the behaviour for x86. if (addr_lhs != addr_rhs && addr_lhs != AddrSpace::none && addr_rhs != AddrSpace::none) { if (is_final_pass()) LOG(WARNING, binop.loc, out_) << "Addrspace mismatch"; binop.type.SetAS(AddrSpace::none); } // Associativity from left to right for binary operator else if (addr_lhs != AddrSpace::none) { binop.type.SetAS(addr_lhs); } else { // In case rhs is none, then this triggers warning in selectProbeReadHelper. binop.type.SetAS(addr_rhs); } if (!is_final_pass()) { return; } if (lht.IsIntTy() && rht.IsIntTy()) { binop_int(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.type != rht.type) { LOG(ERROR, binop.left->loc + binop.right->loc, err_) << "Type mismatch for '" << opstr(binop) << "': comparing '" << lht << "' with '" << rht << "'"; } // Also allow combination like reg("sp") + 8 else if (binop.op != Operator::EQ && binop.op != Operator::NE) { LOG(ERROR, binop.loc, err_) << "The " << opstr(binop) << " operator can not be used on expressions of types " << lht << ", " << rht; } else if (binop.op == Operator::EQ && ((!binop.left->is_literal && binop.right->is_literal) || (binop.left->is_literal && !binop.right->is_literal))) { auto *lit = binop.left->is_literal ? binop.left : binop.right; auto *str = lit == binop.left ? binop.right : binop.left; auto lit_len = bpftrace_.get_string_literal(lit).size(); auto str_len = str->type.GetNumElements(); if (lit_len > str_len) { LOG(WARNING, binop.left->loc + binop.loc + binop.right->loc, out_) << "The literal is longer than the variable string (size=" << str_len << "), condition will always be false"; } } } void SemanticAnalyser::visit(Unop &unop) { if (unop.op == Operator::INCREMENT || unop.op == Operator::DECREMENT) { // Handle ++ and -- before visiting unop.expr, because these // operators should be able to work with undefined maps. if (!unop.expr->is_map && !unop.expr->is_variable) { LOG(ERROR, unop.loc, err_) << "The " << opstr(unop) << " operator must be applied to a map or variable"; } if (unop.expr->is_map) { Map &map = static_cast(*unop.expr); auto *maptype = get_map_type(map); if (!maptype) assign_map_type(map, CreateInt64()); } } unop.expr->accept(*this); auto valid_ptr_op = false; switch (unop.op) { case Operator::INCREMENT: case Operator::DECREMENT: case Operator::MUL: valid_ptr_op = true; break; default:; } SizedType &type = unop.expr->type; if (is_final_pass()) { // Unops are only allowed on ints (e.g. ~$x), dereference only on pointers if (!type.IsIntegerTy() && !(type.IsPtrTy() && valid_ptr_op)) { LOG(ERROR, unop.loc, err_) << "The " << opstr(unop) << " operator can not be used on expressions of type '" << type << "'"; } } if (unop.op == Operator::MUL) { if (type.IsPtrTy()) { unop.type = SizedType(*type.GetPointeeTy()); if (type.IsCtxAccess()) { unop.type.MarkCtxAccess(); unop.type.is_funcarg = type.is_funcarg; unop.type.is_tparg = type.is_tparg; } unop.type.SetAS(type.GetAS()); } else if (type.IsRecordTy()) { LOG(ERROR, unop.loc, err_) << "Can not dereference struct/union of type '" << type.GetName() << "'. It is not a pointer."; } else if (type.IsIntTy()) { unop.type = CreateUInt64(); } } else if (unop.op == Operator::LNOT) { // CreateUInt() abort if a size is invalid, so check the size here if (!(type.GetSize() == 0 || type.GetSize() == 1 || type.GetSize() == 2 || type.GetSize() == 4 || type.GetSize() == 8)) { LOG(ERROR, unop.loc, err_) << "The " << opstr(unop) << " operator can not be used on expressions of type '" << type << "'"; } else { unop.type = CreateUInt(8 * type.GetSize()); } } else if (type.IsPtrTy() && valid_ptr_op) { unop.type = unop.expr->type; } else { unop.type = CreateInteger(64, type.IsSigned()); } } void SemanticAnalyser::visit(Ternary &ternary) { ternary.cond->accept(*this); ternary.left->accept(*this); ternary.right->accept(*this); Type &cond = ternary.cond->type.type; Type &lhs = ternary.left->type.type; Type &rhs = ternary.right->type.type; if (is_final_pass()) { if (lhs != rhs) { LOG(ERROR, ternary.loc, err_) << "Ternary operator must return the same type: " << "have '" << lhs << "' and '" << rhs << "'"; } if (cond != Type::integer) LOG(ERROR, ternary.loc, err_) << "Invalid condition in ternary: " << cond; } if (lhs == Type::string) ternary.type = CreateString(STRING_SIZE); else if (lhs == Type::integer) ternary.type = CreateInteger(64, ternary.left->type.IsSigned()); else if (lhs == Type::none) ternary.type = CreateNone(); else { LOG(ERROR, ternary.loc, err_) << "Ternary return type unsupported " << lhs; } } void SemanticAnalyser::visit(If &if_block) { if_block.cond->accept(*this); if (is_final_pass()) { Type &cond = if_block.cond->type.type; if (cond != Type::integer) LOG(ERROR, if_block.loc, err_) << "Invalid condition in if(): " << cond; } accept_statements(if_block.stmts); if (if_block.else_stmts) accept_statements(if_block.else_stmts); } void SemanticAnalyser::visit(Unroll &unroll) { unroll.expr->accept(*this); auto unroll_value = bpftrace_.get_int_literal(unroll.expr); if (!unroll_value.has_value()) { LOG(ERROR, unroll.loc, err_) << "invalid unroll value"; return; } unroll.var = *unroll_value; if (unroll.var > 100) { LOG(ERROR, unroll.loc, err_) << "unroll maximum value is 100"; } else if (unroll.var < 1) { LOG(ERROR, unroll.loc, err_) << "unroll minimum value is 1"; } accept_statements(unroll.stmts); } void SemanticAnalyser::visit(Jump &jump) { switch (jump.ident) { case JumpType::RETURN: // return can be used outside of loops break; case JumpType::BREAK: case JumpType::CONTINUE: if (!in_loop()) LOG(ERROR, jump.loc, err_) << opstr(jump) << " used outside of a loop"; break; default: LOG(ERROR, jump.loc, err_) << "Unknown jump: '" << opstr(jump) << "'"; } } void SemanticAnalyser::visit(While &while_block) { if (is_final_pass() && !bpftrace_.feature_->has_loop()) { LOG(WARNING, while_block.loc, out_) << "Kernel does not support bounded loops. Depending" " on LLVMs loop unroll to generate loadable code."; } while_block.cond->accept(*this); loop_depth_++; accept_statements(while_block.stmts); loop_depth_--; } void SemanticAnalyser::visit(FieldAccess &acc) { // A field access must have a field XOR index assert((acc.field.size() > 0) != (acc.index >= 0)); acc.expr->accept(*this); SizedType &type = acc.expr->type; if (type.IsPtrTy()) { LOG(ERROR, acc.loc, err_) << "Can not access field '" << acc.field << "' on type '" << type << "'. Try dereferencing it first, or using '->'"; return; } if (!type.IsRecordTy() && !type.IsTupleTy()) { if (is_final_pass()) { std::string field; if (acc.field.size()) field += "field '" + acc.field + "'"; else field += "index " + std::to_string(acc.index); LOG(ERROR, acc.loc, err_) << "Can not access " << field << " on expression of type '" << type << "'"; } return; } if (type.is_funcarg) { auto it = ap_args_.find(acc.field); if (it != ap_args_.end()) { acc.type = it->second; acc.type.SetAS(acc.expr->type.GetAS()); if (is_final_pass()) { if (acc.type.IsNoneTy()) LOG(ERROR, acc.loc, err_) << acc.field << " has unsupported type"; } } else { LOG(ERROR, acc.loc, err_) << "Can't find function parameter " << acc.field; } return; } if (type.IsTupleTy()) { if (acc.index < 0) { LOG(ERROR, acc.loc, err_) << "Tuples must be indexed with a constant and non-negative integer"; return; } bool valid_idx = static_cast(acc.index) < type.GetFields().size(); // We may not have inferred the full type of the tuple yet in early passes // so wait until the final pass. if (!valid_idx && is_final_pass()) LOG(ERROR, acc.loc, err_) << "Invalid tuple index: " << acc.index << ". Found " << type.GetFields().size() << " elements in tuple."; if (valid_idx) acc.type = type.GetField(acc.index).type; return; } if (!bpftrace_.structs.Has(type.GetName())) { LOG(ERROR, acc.loc, err_) << "Unknown struct/union: '" << type.GetName() << "'"; return; } std::map> structs; if (type.is_tparg) { for (AttachPoint *attach_point : *probe_->attach_points) { if (probetype(attach_point->provider) != ProbeType::tracepoint) { // The args builtin can only be used with tracepoint // an error message is already generated in visit(Builtin) // just continue semantic analysis continue; } auto matches = bpftrace_.probe_matcher_->get_matches_for_ap( *attach_point); for (auto &match : matches) { std::string tracepoint_struct = TracepointFormatParser::get_struct_name( match); structs[tracepoint_struct] = bpftrace_.structs.Lookup( tracepoint_struct); } } } else { structs[type.GetName()] = type.GetStruct(); } for (auto it : structs) { std::string cast_type = it.first; const auto record = it.second.lock(); if (!record->HasField(acc.field)) { LOG(ERROR, acc.loc, err_) << "Struct/union of type '" << cast_type << "' does not contain " << "a field named '" << acc.field << "'"; } else { const auto &field = record->GetField(acc.field); acc.type = field.type; if (acc.expr->type.IsCtxAccess() && (acc.type.IsArrayTy() || acc.type.IsRecordTy())) { // e.g., ((struct bpf_perf_event_data*)ctx)->regs.ax acc.type.MarkCtxAccess(); } acc.type.is_internal = type.is_internal; acc.type.SetAS(acc.expr->type.GetAS()); // The kernel uses the first 8 bytes to store `struct pt_regs`. Any // access to the first 8 bytes results in verifier error. if (type.is_tparg && field.offset < 8) LOG(ERROR, acc.loc, err_) << "BPF does not support accessing common tracepoint fields"; } } } void SemanticAnalyser::visit(Cast &cast) { cast.expr->accept(*this); if (cast.expr->type.IsRecordTy()) { LOG(ERROR, cast.loc, err_) << "Cannot cast from struct type \"" << cast.expr->type << "\""; } bool is_ctx = cast.expr->type.IsCtxAccess(); auto &intcasts = getIntcasts(); auto k_v = intcasts.find(cast.cast_type); // Built-in int types if (k_v != intcasts.end()) { auto &v = k_v->second; if (cast.is_pointer) { cast.type = CreatePointer(CreateInteger(std::get<0>(v), std::get<1>(v))); if (is_ctx) { LOG(ERROR, cast.loc, err_) << "Integer pointer casts are not supported for type: ctx"; } if (cast.is_double_pointer) cast.type = CreatePointer(cast.type); } else { cast.type = CreateInteger(std::get<0>(v), std::get<1>(v)); auto rhs = cast.expr->type; // Casting Type::ctx to Type::integer is supported to access a // tracepoint's __data_loc field. See #990 and #770 // In this case, the context information will be lost if (!rhs.IsIntTy() && !rhs.IsRecordTy() && !rhs.IsPtrTy() && !rhs.IsCtxAccess()) { LOG(ERROR, cast.loc, err_) << "Casts are not supported for type: \"" << rhs << "\""; } } // Consider both case *(int8)(retval) and *(int8*)retval cast.type.SetAS(cast.expr->type.GetAS()); return; } if (!bpftrace_.structs.Has(cast.cast_type)) { LOG(ERROR, cast.loc, err_) << "Unknown struct/union: '" << cast.cast_type << "'"; return; } SizedType struct_type = CreateRecord( cast.cast_type, bpftrace_.structs.Lookup(cast.cast_type)); if (cast.is_pointer) { cast.type = CreatePointer(struct_type); if (cast.is_double_pointer) cast.type = CreatePointer(cast.type); } else { LOG(ERROR, cast.loc, err_) << "Cannot cast to struct type \"" << cast.cast_type << "\""; } if (is_ctx) cast.type.MarkCtxAccess(); cast.type.SetAS(cast.expr->type.GetAS()); // case : BEGIN { @foo = (struct Foo)0; } // case : profile:hz:99 $task = (struct task_struct *)curtask. if (cast.type.GetAS() == AddrSpace::none) { ProbeType type = single_provider_type(); cast.type.SetAS(find_addrspace(type)); } } void SemanticAnalyser::visit(Tuple &tuple) { std::vector elements; for (size_t i = 0; i < tuple.elems->size(); ++i) { Expression *elem = tuple.elems->at(i); elem->accept(*this); // 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; elements.emplace_back(elem->type); } tuple.type = CreateTuple(bpftrace_.structs.AddTuple(elements)); } void SemanticAnalyser::visit(ExprStatement &expr) { expr.expr->accept(*this); } void SemanticAnalyser::visit(AssignMapStatement &assignment) { assignment.map->accept(*this); assignment.expr->accept(*this); assign_map_type(*assignment.map, assignment.expr->type); const std::string &map_ident = assignment.map->ident; auto type = assignment.expr->type; if (type.IsRecordTy()) { std::string ty = assignment.expr->type.GetName(); std::string stored_ty = map_val_[map_ident].GetName(); if (!stored_ty.empty() && stored_ty != ty) { LOG(ERROR, assignment.loc, err_) << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << ty << "' when map already contains a value of type '" << stored_ty << "''"; } else { map_val_[map_ident] = assignment.expr->type; map_val_[map_ident].is_internal = true; } } else if (type.IsStringTy()) { auto map_size = map_val_[map_ident].GetSize(); auto expr_size = assignment.expr->type.GetSize(); if (map_size != expr_size) { std::stringstream buf; buf << "String size mismatch: " << map_size << " != " << expr_size << "."; if (map_size < expr_size) { buf << " The value may be truncated."; LOG(WARNING, assignment.loc, out_) << buf.str(); } else { // bpf_map_update_elem() expects map_size-length value LOG(ERROR, assignment.loc, err_) << buf.str(); } } } else if (type.IsBufferTy()) { auto map_size = map_val_[map_ident].GetSize(); auto expr_size = assignment.expr->type.GetSize(); if (map_size != expr_size) { std::stringstream buf; buf << "Buffer size mismatch: " << map_size << " != " << expr_size << "."; if (map_size < expr_size) { buf << " The value may be truncated."; LOG(WARNING, assignment.loc, out_) << buf.str(); } else { // bpf_map_update_elem() expects map_size-length value LOG(ERROR, assignment.loc, err_) << buf.str(); } } } else if (type.IsCtxAccess()) { // bpf_map_update_elem() only accepts a pointer to a element in the stack LOG(ERROR, assignment.loc, err_) << "context cannot be assigned to a map"; } else if (type.IsTupleTy()) { // Early passes may not have been able to deduce the full types of tuple // elements yet. So wait until final pass. if (is_final_pass()) { const auto &map_type = map_val_[map_ident]; const auto &expr_type = assignment.expr->type; if (map_type != expr_type) { LOG(ERROR, assignment.loc, err_) << "Tuple type mismatch: " << map_type << " != " << expr_type << "."; } } } else if (type.IsArrayTy()) { const auto &map_type = map_val_[map_ident]; const auto &expr_type = assignment.expr->type; if (map_type == expr_type) { map_val_[map_ident].is_internal = true; } } if (is_final_pass()) { if (type.IsNoneTy()) LOG(ERROR, assignment.expr->loc, err_) << "Invalid expression for assignment: " << type; } } void SemanticAnalyser::visit(AssignVarStatement &assignment) { assignment.expr->accept(*this); std::string var_ident = assignment.var->ident; auto search = variable_val_.find(var_ident); assignment.var->type = assignment.expr->type; auto *builtin = dynamic_cast(assignment.expr); if (builtin && builtin->ident == "args" && builtin->type.is_funcarg) { LOG(ERROR, assignment.loc, err_) << "args cannot be assigned to a variable"; } if (search != variable_val_.end()) { if (search->second.IsNoneTy()) { if (is_final_pass()) { LOG(ERROR, assignment.loc, err_) << "Undefined variable: " + var_ident; } else { search->second = assignment.expr->type; } } else if (!search->second.IsSameType(assignment.expr->type)) { LOG(ERROR, assignment.loc, err_) << "Type mismatch for " << var_ident << ": " << "trying to assign value of type '" << assignment.expr->type << "' when variable already contains a value of type '" << search->second << "'"; } } else { // This variable hasn't been seen before variable_val_[var_ident] = assignment.expr->type; assignment.var->type = assignment.expr->type; } auto &storedTy = variable_val_[var_ident]; auto &assignTy = assignment.expr->type; if (assignTy.IsRecordTy()) { if (assignTy.GetName() != storedTy.GetName()) { LOG(ERROR, assignment.loc, err_) << "Type mismatch for " << var_ident << ": " << "trying to assign value of type '" << assignTy.GetName() << "' when variable already contains a value of type '" << storedTy; } } else if (assignTy.IsStringTy()) { auto var_size = storedTy.GetSize(); auto expr_size = assignTy.GetSize(); if (var_size != expr_size) { LOG(WARNING, assignment.loc, out_) << "String size mismatch: " << var_size << " != " << expr_size << (var_size < expr_size ? ". The value may be truncated." : ". The value may contain garbage."); } } else if (assignTy.IsBufferTy()) { auto var_size = storedTy.GetSize(); auto expr_size = assignTy.GetSize(); if (var_size != expr_size) { LOG(WARNING, assignment.loc, out_) << "Buffer size mismatch: " << var_size << " != " << expr_size << (var_size < expr_size ? ". The value may be truncated." : ". The value may contain garbage."); } } else if (assignTy.IsTupleTy()) { // 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()) { auto var_type = storedTy; auto expr_type = assignTy; if (var_type != expr_type) { LOG(ERROR, assignment.loc, err_) << "Tuple type mismatch: " << var_type << " != " << expr_type << "."; } } } if (is_final_pass()) { auto &ty = assignTy.type; if (ty == Type::none) LOG(ERROR, assignment.expr->loc, err_) << "Invalid expression for assignment: " << ty; } } void SemanticAnalyser::visit(Predicate &pred) { pred.expr->accept(*this); if (is_final_pass()) { SizedType &ty = pred.expr->type; if (!ty.IsIntTy() && !ty.IsPtrTy()) { LOG(ERROR, pred.loc, err_) << "Invalid type for predicate: " << pred.expr->type.type; } } } void SemanticAnalyser::visit(AttachPoint &ap) { ap.provider = probetypeName(ap.provider); if (ap.provider == "kprobe" || ap.provider == "kretprobe") { if (ap.target != "") LOG(ERROR, ap.loc, err_) << "kprobes should not have a target"; if (ap.func == "") LOG(ERROR, ap.loc, err_) << "kprobes should be attached to a function"; if (is_final_pass()) { // Warn if user tries to attach to a non-traceable function if (!has_wildcard(ap.func) && !bpftrace_.is_traceable_func(ap.func)) { LOG(WARNING, ap.loc, out_) << ap.func << " is not traceable (either non-existing, inlined, or marked as " "\"notrace\"); attaching to it will likely fail"; } } } else if (ap.provider == "uprobe" || ap.provider == "uretprobe") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << ap.provider << " should have a target"; if (ap.func == "" && ap.address == 0) LOG(ERROR, ap.loc, err_) << ap.provider << " should be attached to a function and/or address"; if (ap.provider == "uretprobe" && ap.func_offset != 0) LOG(ERROR, ap.loc, err_) << "uretprobes can not be attached to a function offset"; auto paths = resolve_binary_path(ap.target, bpftrace_.pid()); switch (paths.size()) { case 0: LOG(ERROR, ap.loc, err_) << "uprobe target file '" << ap.target << "' does not exist or is not executable"; break; case 1: ap.target = paths.front(); break; default: // If we are doing a PATH lookup (ie not glob), we follow shell // behavior and take the first match. // Otherwise we keep the target with glob, it will be expanded later if (ap.target.find("*") == std::string::npos) { LOG(WARNING, ap.loc, out_) << "attaching to uprobe target file '" << paths.front() << "' but matched " << std::to_string(paths.size()) << " binaries"; ap.target = paths.front(); } } // Check if there were some arguments resolved for the probe auto args_it = bpftrace_.ap_args_.find(probe_->name()); if (args_it != bpftrace_.ap_args_.end()) { ap_args_.clear(); ap_args_.insert(args_it->second.begin(), args_it->second.end()); } } else if (ap.provider == "usdt") { bpftrace_.has_usdt_ = true; if (ap.func == "") LOG(ERROR, ap.loc, err_) << "usdt probe must have a target function or wildcard"; if (ap.target != "" && !(bpftrace_.pid() > 0 && has_wildcard(ap.target))) { auto paths = resolve_binary_path(ap.target, bpftrace_.pid()); switch (paths.size()) { case 0: LOG(ERROR, ap.loc, err_) << "usdt target file '" << ap.target << "' does not exist or is not executable"; break; case 1: ap.target = paths.front(); break; default: // If we are doing a PATH lookup (ie not glob), we follow shell // behavior and take the first match. // Otherwise we keep the target with glob, it will be expanded later if (ap.target.find("*") == std::string::npos) { LOG(WARNING, ap.loc, out_) << "attaching to usdt target file '" << paths.front() << "' but matched " << std::to_string(paths.size()) << " binaries"; ap.target = paths.front(); } } } if (bpftrace_.pid() > 0) { USDTHelper::probes_for_pid(bpftrace_.pid()); } else if (ap.target != "") { for (auto &path : resolve_binary_path(ap.target)) USDTHelper::probes_for_path(path); } else { LOG(ERROR, ap.loc, err_) << "usdt probe must specify at least path or pid to probe"; } } else if (ap.provider == "tracepoint") { if (ap.target == "" || ap.func == "") LOG(ERROR, ap.loc, err_) << "tracepoint probe must have a target"; } else if (ap.provider == "profile") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "profile probe must have unit of time"; else if (ap.target != "hz" && ap.target != "us" && ap.target != "ms" && ap.target != "s") LOG(ERROR, ap.loc, err_) << ap.target << " is not an accepted unit of time"; if (ap.func != "") LOG(ERROR, ap.loc, err_) << "profile probe must have an integer frequency"; else if (ap.freq <= 0) LOG(ERROR, ap.loc, err_) << "profile frequency should be a positive integer"; } else if (ap.provider == "interval") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "interval probe must have unit of time"; else if (ap.target != "ms" && ap.target != "s" && ap.target != "us" && ap.target != "hz") LOG(ERROR, ap.loc, err_) << ap.target << " is not an accepted unit of time"; if (ap.func != "") LOG(ERROR, ap.loc, err_) << "interval probe must have an integer frequency"; } else if (ap.provider == "software") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "software probe must have a software event name"; else { if (!has_wildcard(ap.target) && !ap.ignore_invalid) { bool found = false; for (auto &probeListItem : SW_PROBE_LIST) { if (ap.target == probeListItem.path || (!probeListItem.alias.empty() && ap.target == probeListItem.alias)) { found = true; break; } } if (!found) LOG(ERROR, ap.loc, err_) << ap.target << " is not a software probe"; } else if (!listing_) { LOG(ERROR, ap.loc, err_) << "wildcards are not allowed for hardware probe type"; } } if (ap.func != "") LOG(ERROR, ap.loc, err_) << "software probe can only have an integer count"; else if (ap.freq < 0) LOG(ERROR, ap.loc, err_) << "software count should be a positive integer"; } else if (ap.provider == "watchpoint" || ap.provider == "asyncwatchpoint") { if (ap.func.size()) { if (bpftrace_.pid() <= 0 && !has_child_) LOG(ERROR, ap.loc, err_) << "-p PID or -c CMD required for watchpoint"; if (ap.address > static_cast(arch::max_arg())) LOG(ERROR, ap.loc, err_) << arch::name() << " doesn't support arg" << ap.address; } else if (ap.provider == "asyncwatchpoint") LOG(ERROR, ap.loc, err_) << ap.provider << " requires a function name"; else if (!ap.address) LOG(ERROR, ap.loc, err_) << "watchpoint must be attached to a non-zero address"; if (ap.len != 1 && ap.len != 2 && ap.len != 4 && ap.len != 8) LOG(ERROR, ap.loc, err_) << "watchpoint length must be one of (1,2,4,8)"; if (ap.mode.empty()) LOG(ERROR, ap.loc, err_) << "watchpoint mode must be combination of (r,w,x)"; std::sort(ap.mode.begin(), ap.mode.end()); for (const char c : ap.mode) { if (c != 'r' && c != 'w' && c != 'x') LOG(ERROR, ap.loc, err_) << "watchpoint mode must be combination of (r,w,x)"; } for (size_t i = 1; i < ap.mode.size(); ++i) { if (ap.mode[i - 1] == ap.mode[i]) LOG(ERROR, ap.loc, err_) << "watchpoint modes may not be duplicated"; } const auto invalid_modes = arch::invalid_watchpoint_modes(); if (std::any_of(invalid_modes.cbegin(), invalid_modes.cend(), [&](const auto &mode) { return mode == ap.mode; })) LOG(ERROR, ap.loc, err_) << "invalid watchpoint mode: " << ap.mode; } else if (ap.provider == "hardware") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "hardware probe must have a hardware event name"; else { if (!has_wildcard(ap.target) && !ap.ignore_invalid) { bool found = false; for (auto &probeListItem : HW_PROBE_LIST) { if (ap.target == probeListItem.path || (!probeListItem.alias.empty() && ap.target == probeListItem.alias)) { found = true; break; } } if (!found) LOG(ERROR, ap.loc, err_) << ap.target + " is not a hardware probe"; } else if (!listing_) { LOG(ERROR, ap.loc, err_) << "wildcards are not allowed for hardware probe type"; } } if (ap.func != "") LOG(ERROR, ap.loc, err_) << "hardware probe can only have an integer count"; else if (ap.freq < 0) LOG(ERROR, ap.loc, err_) << "hardware frequency should be a positive integer"; } else if (ap.provider == "BEGIN" || ap.provider == "END") { if (ap.target != "" || ap.func != "") LOG(ERROR, ap.loc, err_) << "BEGIN/END probes should not have a target"; if (is_final_pass()) { if (ap.provider == "BEGIN") { if (has_begin_probe_) LOG(ERROR, ap.loc, err_) << "More than one BEGIN probe defined"; has_begin_probe_ = true; } if (ap.provider == "END") { if (has_end_probe_) LOG(ERROR, ap.loc, err_) << "More than one END probe defined"; has_end_probe_ = true; } } } else if (ap.provider == "kfunc" || ap.provider == "kretfunc") { #ifndef HAVE_BCC_KFUNC LOG(ERROR, ap.loc, err_) << "kfunc/kretfunc not available for your linked against bcc version."; return; #endif bool supported = bpftrace_.feature_->has_prog_kfunc() && bpftrace_.btf_.has_data(); if (!supported) { LOG(ERROR, ap.loc, err_) << "kfunc/kretfunc not available for your kernel version."; return; } if (ap.func == "") LOG(ERROR, ap.loc, err_) << "kfunc should specify a function"; if (!listing_) { const auto &ap_map = bpftrace_.ap_args_; auto it = ap_map.find(probe_->name()); if (it != ap_map.end()) { auto args = it->second; ap_args_.clear(); ap_args_.insert(args.begin(), args.end()); } } } else if (ap.provider == "iter") { bool supported = false; if (ap.func == "task") { supported = bpftrace_.feature_->has_prog_iter_task() && bpftrace_.btf_.has_data(); } else if (ap.func == "task_file") { supported = bpftrace_.feature_->has_prog_iter_task_file() && bpftrace_.btf_.has_data(); } else if (listing_) { supported = true; } if (!supported) { LOG(ERROR, ap.loc, err_) << "iter " << ap.func << " not available for your kernel version."; } } else { LOG(ERROR, ap.loc, err_) << "Invalid provider: '" << ap.provider << "'"; } } void SemanticAnalyser::visit(Probe &probe) { auto aps = probe.attach_points->size(); // Clear out map of variable names - variables should be probe-local variable_val_.clear(); probe_ = &probe; for (AttachPoint *ap : *probe.attach_points) { if (!listing_ && aps > 1 && ap->provider == "iter") { LOG(ERROR, ap->loc, err_) << "Only single iter attach point is allowed."; return; } ap->accept(*this); } if (probe.pred) { probe.pred->accept(*this); } if (probe.stmts) { for (Statement *stmt : *probe.stmts) { stmt->accept(*this); } } } void SemanticAnalyser::visit(Program &program) { for (Probe *probe : *program.probes) probe->accept(*this); } int SemanticAnalyser::analyse() { // Multiple passes to handle variables being used before they are defined std::string errors; int num_passes = listing_ ? 1 : num_passes_; for (pass_ = 1; pass_ <= num_passes; pass_++) { root_->accept(*this); errors = err_.str(); if (!errors.empty()) { out_ << errors; return pass_; } } return 0; } bool SemanticAnalyser::is_final_pass() const { return pass_ == num_passes_; } bool SemanticAnalyser::check_assignment(const Call &call, bool want_map, bool want_var, bool want_map_key) { if (want_map && want_var && want_map_key) { if (!call.map && !call.var && !call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a map or a " "variable, or be used as a map key"; return false; } } else if (want_map && want_var) { if (!call.map && !call.var) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a map or a variable"; return false; } } else if (want_map && want_map_key) { if (!call.map && !call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a map or be used as a map key"; return false; } } else if (want_var && want_map_key) { if (!call.var && !call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a variable or be used as a map key"; return false; } } else if (want_map) { if (!call.map) { LOG(ERROR, call.loc, err_) << call.func << "() should be directly assigned to a map"; return false; } } else if (want_var) { if (!call.var) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a variable"; return false; } } else if (want_map_key) { if (!call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be used as a map key"; return false; } } else { if (call.map || call.var || call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should not be used in an assignment or as a map key"; return false; } } return true; } bool SemanticAnalyser::check_nargs(const Call &call, size_t expected_nargs) { std::stringstream err; std::vector::size_type nargs = 0; if (call.vargs) nargs = call.vargs->size(); if (nargs != expected_nargs) { if (expected_nargs == 0) err << call.func << "() requires no arguments"; else if (expected_nargs == 1) err << call.func << "() requires one argument"; else err << call.func << "() requires " << expected_nargs << " arguments"; err << " (" << nargs << " provided)"; LOG(ERROR, call.loc, err_) << err.str(); return false; } return true; } bool SemanticAnalyser::check_varargs(const Call &call, size_t min_nargs, size_t max_nargs) { std::vector::size_type nargs = 0; std::stringstream err; if (call.vargs) nargs = call.vargs->size(); if (nargs < min_nargs) { if (min_nargs == 1) err << call.func << "() requires at least one argument"; else err << call.func << "() requires at least " << min_nargs << " arguments"; err << " (" << nargs << " provided)"; LOG(ERROR, call.loc, err_) << err.str(); return false; } else if (nargs > max_nargs) { if (max_nargs == 0) err << call.func << "() requires no arguments"; else if (max_nargs == 1) err << call.func << "() takes up to one argument"; else err << call.func << "() takes up to " << max_nargs << " arguments"; err << " (" << nargs << " provided)"; LOG(ERROR, call.loc, err_) << err.str(); return false; } return true; } bool SemanticAnalyser::check_arg(const Call &call, Type type, int arg_num, bool want_literal, bool fail) { if (!call.vargs) return false; auto &arg = *call.vargs->at(arg_num); if (want_literal && (!arg.is_literal || arg.type.type != type)) { if (fail) { LOG(ERROR, call.loc, err_) << call.func << "() expects a " << type << " literal (" << arg.type.type << " provided)"; if (type == Type::string) { // If the call requires a string literal and a positional parameter is // given, tell user to use str() auto *pos_param = dynamic_cast(&arg); if (pos_param) LOG(ERROR) << "Use str($" << pos_param->n << ") to treat $" << pos_param->n << " as a string"; } } return false; } else if (is_final_pass() && arg.type.type != type) { if (fail) { LOG(ERROR, call.loc, err_) << call.func << "() only supports " << type << " arguments (" << arg.type.type << " provided)"; } return false; } return true; } bool SemanticAnalyser::check_symbol(const Call &call, int arg_num __attribute__((unused))) { if (!call.vargs) return false; auto arg = bpftrace_.get_string_literal(call.vargs->at(0)); std::string re = "^[a-zA-Z0-9./_-]+$"; bool is_valid = std::regex_match(arg, std::regex(re)); if (!is_valid) { LOG(ERROR, call.loc, err_) << call.func << "() expects a string that is a valid symbol (" << re << ") as input (\"" << arg << "\" provided)"; return false; } return true; } bool SemanticAnalyser::check_available(const Call &call, const AttachPoint &ap) { auto &func = call.func; ProbeType type = probetype(ap.provider); if (func == "reg") { switch (type) { case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::profile: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: return true; case ProbeType::invalid: case ProbeType::tracepoint: case ProbeType::kfunc: case ProbeType::kretfunc: case ProbeType::iter: return false; } } else if (func == "uaddr") { switch (type) { case ProbeType::usdt: case ProbeType::uretprobe: case ProbeType::uprobe: return true; case ProbeType::invalid: 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::kfunc: case ProbeType::kretfunc: case ProbeType::iter: return false; } } else if (func == "signal") { if (ap.provider == "BEGIN" || ap.provider == "END") return false; 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::kfunc: case ProbeType::kretfunc: return true; case ProbeType::invalid: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::iter: return false; } } if (type == ProbeType::invalid) return false; return true; } SizedType *SemanticAnalyser::get_map_type(const Map &map) { const std::string &map_ident = map.ident; auto search = map_val_.find(map_ident); if (search == map_val_.end()) return nullptr; return &search->second; } void SemanticAnalyser::update_assign_map_type(const Map &map, SizedType &type, const SizedType &new_type) { const std::string &map_ident = map.ident; if ((type.IsTupleTy() && new_type.IsTupleTy() && type.GetFields().size() != new_type.GetFields().size()) || (type.type != new_type.type) || (type.IsRecordTy() && type.GetName() != new_type.GetName()) || (type.IsArrayTy() && type != new_type)) { LOG(ERROR, map.loc, err_) << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << new_type << "' when map already contains a value of type '" << type; return; } // all integers are 64bit if (type.IsIntTy()) return; if (type.IsTupleTy() && new_type.IsTupleTy()) { auto &fields = type.GetFields(); auto &new_fields = new_type.GetFields(); for (size_t i = 0; i < fields.size(); i++) { update_assign_map_type(map, fields[i].type, new_fields[i].type); } } type = new_type; } /* * assign_map_type * * Semantic analysis for assigning a value of the provided type * to the given map. */ void SemanticAnalyser::assign_map_type(const Map &map, const SizedType &type) { const std::string &map_ident = map.ident; auto *maptype = get_map_type(map); if (maptype) { if (maptype->IsNoneTy()) { if (is_final_pass()) LOG(ERROR, map.loc, err_) << "Undefined map: " + map_ident; else *maptype = type; } else if (maptype->type != type.type) { LOG(ERROR, map.loc, err_) << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << type << "' when map already contains a value of type '" << *maptype; } update_assign_map_type(map, *maptype, type); } else { // This map hasn't been seen before map_val_.insert({ map_ident, type }); if (map_val_[map_ident].IsIntTy()) { // Store all integer values as 64-bit in maps, so that there will // be space for any integer to be assigned to the map later map_val_[map_ident].SetSize(8); } } } void SemanticAnalyser::accept_statements(StatementList *stmts) { for (size_t i = 0; i < stmts->size(); i++) { auto stmt = stmts->at(i); stmt->accept(*this); if (is_final_pass()) { auto *jump = dynamic_cast(stmt); if (jump && i < (stmts->size() - 1)) { LOG(WARNING, jump->loc, out_) << "All code after a '" << opstr(*jump) << "' is unreachable."; } } } } Pass CreateSemanticPass() { auto fn = [](Node &n, PassContext &ctx) { auto semantics = SemanticAnalyser(&n, ctx.b, !ctx.b.cmd_.empty()); int err = semantics.analyse(); if (err) return PassResult::Error("Semantic", err); return PassResult::Success(); }; return Pass("Semantic", fn); }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/passes/semantic_analyser.h000066400000000000000000000101141413460502400221040ustar00rootroot00000000000000#pragma once #include #include #include #include "bpffeature.h" #include "bpftrace.h" #include "map.h" #include "pass_manager.h" #include "types.h" #include "visitors.h" namespace bpftrace { namespace ast { class SemanticAnalyser : public Visitor { public: explicit SemanticAnalyser(Node *root, BPFtrace &bpftrace, std::ostream &out = std::cerr, bool has_child = true, bool listing = false) : root_(root), bpftrace_(bpftrace), out_(out), listing_(listing), has_child_(has_child) { } explicit SemanticAnalyser(Node *root, BPFtrace &bpftrace, bool has_child) : SemanticAnalyser(root, bpftrace, std::cerr, has_child) { } explicit SemanticAnalyser(Node *root, BPFtrace &bpftrace, bool has_child, bool listing) : SemanticAnalyser(root, bpftrace, std::cerr, has_child, listing) { } void visit(Integer &integer) override; void visit(PositionalParameter ¶m) override; void visit(String &string) override; void visit(StackMode &mode) override; void visit(Identifier &identifier) override; void visit(Builtin &builtin) override; void visit(Call &call) override; void visit(Map &map) override; void visit(Variable &var) override; void visit(Binop &binop) override; void visit(Unop &unop) override; void visit(While &while_block) override; void visit(Jump &jump) override; void visit(Ternary &ternary) override; void visit(FieldAccess &acc) override; void visit(ArrayAccess &arr) override; void visit(Cast &cast) override; void visit(Tuple &tuple) override; void visit(ExprStatement &expr) override; void visit(AssignMapStatement &assignment) override; void visit(AssignVarStatement &assignment) override; void visit(If &if_block) override; void visit(Unroll &unroll) override; void visit(Predicate &pred) override; void visit(AttachPoint &ap) override; void visit(Probe &probe) override; void visit(Program &program) override; int analyse(); private: Node *root_ = nullptr; BPFtrace &bpftrace_; std::ostream &out_; std::ostringstream err_; int pass_; const int num_passes_ = 10; bool listing_; bool is_final_pass() const; bool check_assignment(const Call &call, bool want_map, bool want_var, bool want_map_key); bool check_nargs(const Call &call, size_t expected_nargs); bool check_varargs(const Call &call, size_t min_nargs, size_t max_nargs); bool check_arg(const Call &call, Type type, int arg_num, bool want_literal = false, bool fail = true); bool check_symbol(const Call &call, int arg_num); bool check_available(const Call &call, const AttachPoint &ap); void check_stack_call(Call &call, bool kernel); SizedType *get_map_type(const Map &map); void assign_map_type(const Map &map, const SizedType &type); void update_assign_map_type(const Map &map, SizedType &type, const SizedType &new_type); void builtin_args_tracepoint(AttachPoint *attach_point, Builtin &builtin); ProbeType single_provider_type(void); AddrSpace find_addrspace(ProbeType pt); void binop_ptr(Binop &op); void binop_int(Binop &op); bool in_loop(void) { return loop_depth_ > 0; }; void accept_statements(StatementList *stmts); Probe *probe_; // 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; std::map variable_val_; std::map map_val_; std::map map_key_; ProbeArgs ap_args_; uint32_t loop_depth_ = 0; bool has_begin_probe_ = false; bool has_end_probe_ = false; bool has_child_ = false; bool has_pos_param_ = false; }; Pass CreateSemanticPass(); } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/signal.cpp000066400000000000000000000023421413460502400167210ustar00rootroot00000000000000#include "signal_bt.h" #include #include namespace bpftrace { static std::map signals = { { "SIGABRT", SIGABRT }, { "SIGALRM", SIGALRM }, { "SIGBUS", SIGBUS }, { "SIGCHLD", SIGCHLD }, { "SIGCONT", SIGCONT }, { "SIGFPE", SIGFPE }, { "SIGHUP", SIGHUP }, { "SIGILL", SIGILL }, { "SIGINT", SIGINT }, { "SIGKILL", SIGKILL }, { "SIGPIPE", SIGPIPE }, { "SIGPOLL", SIGPOLL }, { "SIGQUIT", SIGQUIT }, { "SIGSEGV", SIGSEGV }, { "SIGSTOP", SIGSTOP }, { "SIGSYS", SIGSYS }, { "SIGTERM", SIGTERM }, { "SIGTRAP", SIGTRAP }, { "SIGTSTP", SIGTSTP }, { "SIGTTIN", SIGTTIN }, { "SIGTTOU", SIGTTOU }, { "SIGURG", SIGURG }, { "SIGUSR1", SIGUSR1 }, { "SIGUSR2", SIGUSR2 }, { "SIGVTALRM", SIGVTALRM }, { "SIGWINCH", SIGWINCH }, { "SIGXCPU", SIGXCPU }, { "SIGXFSZ", SIGXFSZ }, }; int signal_name_to_num(const std::string &signal) { if (signal.empty()) { return -1; } std::string sig(signal); std::for_each(sig.begin(), sig.end(), [](char &c) { c = ::toupper(c); }); if (sig[0] != 'S') { sig.insert(0, "SIG"); } auto s = signals.find(sig); if (s != signals.end()) return s->second; return -1; } } // namespace bpftrace bpftrace-0.14.0/src/ast/signal_bt.h000066400000000000000000000003341413460502400170520ustar00rootroot00000000000000// 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.14.0/src/ast/visitors.cpp000066400000000000000000000157311413460502400173340ustar00rootroot00000000000000#include "visitors.h" #include "ast.h" namespace bpftrace { namespace ast { void Visitor::visit(Integer &integer __attribute__((__unused__))) { } void Visitor::visit(PositionalParameter ¶m __attribute__((__unused__))) { } void Visitor::visit(String &string __attribute__((__unused__))) { } void Visitor::visit(StackMode &mode __attribute__((__unused__))) { } void Visitor::visit(Builtin &builtin __attribute__((__unused__))) { } void Visitor::visit(Identifier &identifier __attribute__((__unused__))) { } void Visitor::visit(Call &call) { if (call.vargs) { for (Expression *expr : *call.vargs) { Visit(*expr); } } } void Visitor::visit(Map &map) { if (map.vargs) { for (Expression *expr : *map.vargs) { Visit(*expr); } } } void Visitor::visit(Variable &var __attribute__((__unused__))) { } void Visitor::visit(Binop &binop) { Visit(*binop.left); Visit(*binop.right); } void Visitor::visit(Unop &unop) { Visit(*unop.expr); } void Visitor::visit(Ternary &ternary) { Visit(*ternary.cond); Visit(*ternary.left); Visit(*ternary.right); } void Visitor::visit(FieldAccess &acc) { Visit(*acc.expr); } void Visitor::visit(ArrayAccess &arr) { Visit(*arr.expr); Visit(*arr.indexpr); } void Visitor::visit(Cast &cast) { Visit(*cast.expr); } void Visitor::visit(Tuple &tuple) { for (Expression *expr : *tuple.elems) Visit(*expr); } void Visitor::visit(ExprStatement &expr) { Visit(*expr.expr); } void Visitor::visit(AssignMapStatement &assignment) { Visit(*assignment.map); Visit(*assignment.expr); } void Visitor::visit(AssignVarStatement &assignment) { Visit(*assignment.var); Visit(*assignment.expr); } void Visitor::visit(If &if_block) { Visit(*if_block.cond); for (Statement *stmt : *if_block.stmts) { Visit(*stmt); } if (if_block.else_stmts) { for (Statement *stmt : *if_block.else_stmts) { Visit(*stmt); } } } void Visitor::visit(Unroll &unroll) { Visit(*unroll.expr); for (Statement *stmt : *unroll.stmts) { Visit(*stmt); } } void Visitor::visit(While &while_block) { Visit(*while_block.cond); for (Statement *stmt : *while_block.stmts) { Visit(*stmt); } } void Visitor::visit(Jump &jump __attribute__((__unused__))) { } void Visitor::visit(Predicate &pred) { Visit(*pred.expr); } void Visitor::visit(AttachPoint &ap __attribute__((__unused__))) { } void Visitor::visit(Probe &probe) { for (AttachPoint *ap : *probe.attach_points) { Visit(*ap); } if (probe.pred) { Visit(*probe.pred); } for (Statement *stmt : *probe.stmts) { Visit(*stmt); } } void Visitor::visit(Program &program) { for (Probe *probe : *program.probes) Visit(*probe); } template T *Mutator::Value(Node *n) { return reinterpret_cast(Visit(*n)); } ExpressionList *Mutator::mutateExprList(ExpressionList *src) { auto dst = new ExpressionList; for (auto expr : *src) dst->push_back(Value(expr)); return dst; } StatementList *Mutator::mutateStmtList(StatementList *src) { auto dst = new StatementList; for (auto expr : *src) dst->push_back(Value(expr)); return dst; } #define DEFINE_MUTATOR_LEAF(OP) \ Node *Mutator::visit(OP &v) \ { \ return v.leafcopy(); \ } DEFINE_MUTATOR_LEAF(Integer) DEFINE_MUTATOR_LEAF(PositionalParameter) DEFINE_MUTATOR_LEAF(String) DEFINE_MUTATOR_LEAF(StackMode) DEFINE_MUTATOR_LEAF(Builtin) DEFINE_MUTATOR_LEAF(Identifier) DEFINE_MUTATOR_LEAF(Variable) DEFINE_MUTATOR_LEAF(Jump) DEFINE_MUTATOR_LEAF(AttachPoint) #undef DEFINE_MUTATOR_LEAF Node *Mutator::visit(Call &call) { auto c = call.leafcopy(); if (call.vargs) c->vargs = mutateExprList(call.vargs); return c; } Node *Mutator::visit(Map &map) { auto m = map.leafcopy(); if (map.vargs) { m->vargs = mutateExprList(map.vargs); for (auto expr : *m->vargs) expr->key_for_map = m; } return m; } Node *Mutator::visit(Binop &binop) { auto b = binop.leafcopy(); b->left = Value(binop.left); b->right = Value(binop.right); return b; } Node *Mutator::visit(Unop &unop) { auto u = unop.leafcopy(); u->expr = Value(unop.expr); return u; } Node *Mutator::visit(Ternary &ternary) { auto cond = Value(ternary.cond); auto left = Value(ternary.left); auto right = Value(ternary.right); return new Ternary(cond, left, right, ternary.loc); } Node *Mutator::visit(FieldAccess &acc) { auto f = acc.leafcopy(); f->expr = Value(acc.expr); return f; } Node *Mutator::visit(ArrayAccess &arr) { auto a = arr.leafcopy(); a->expr = Value(arr.expr); a->indexpr = Value(arr.indexpr); return a; } Node *Mutator::visit(Cast &cast) { auto c = cast.leafcopy(); c->expr = Value(cast.expr); return c; } Node *Mutator::visit(Tuple &tuple) { auto t = tuple.leafcopy(); t->elems = mutateExprList(tuple.elems); return t; } Node *Mutator::visit(ExprStatement &expr) { auto e = expr.leafcopy(); e->expr = Value(expr.expr); return e; } Node *Mutator::visit(AssignMapStatement &assignment) { auto a = assignment.leafcopy(); a->map = Value(assignment.map); a->expr = Value(assignment.expr); a->expr->map = a->map; return a; } Node *Mutator::visit(AssignVarStatement &assignment) { auto a = assignment.leafcopy(); a->var = Value(assignment.var); a->expr = Value(assignment.expr); return a; } Node *Mutator::visit(If &if_block) { auto i = if_block.leafcopy(); i->cond = Value(if_block.cond); i->stmts = mutateStmtList(if_block.stmts); if (if_block.else_stmts) i->else_stmts = mutateStmtList(if_block.else_stmts); return i; } Node *Mutator::visit(Unroll &unroll) { auto u = unroll.leafcopy(); u->expr = Value(unroll.expr); u->stmts = mutateStmtList(unroll.stmts); return u; } Node *Mutator::visit(While &while_block) { auto w = while_block.leafcopy(); w->cond = Value(while_block.cond); w->stmts = mutateStmtList(while_block.stmts); return w; } Node *Mutator::visit(Probe &probe) { auto p = probe.leafcopy(); p->attach_points = new AttachPointList; for (AttachPoint *ap : *probe.attach_points) p->attach_points->push_back(Value(ap)); if (probe.pred) p->pred = Value(probe.pred); p->stmts = mutateStmtList(probe.stmts); return p; } Node *Mutator::visit(Program &program) { auto p = program.leafcopy(); p->probes = new ProbeList; for (Probe *probe : *program.probes) p->probes->push_back(Value(probe)); return p; } Node *Mutator::visit(Predicate &pred) { auto p = pred.leafcopy(); p->expr = Value(pred.expr); return p; } } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/visitors.h000066400000000000000000000202171413460502400167740ustar00rootroot00000000000000#pragma once #include "vtable.h" #include #include "ast.h" namespace bpftrace { namespace ast { /** Base visitor for double dispatch based visitation */ class VisitorBase { public: virtual ~VisitorBase() = default; virtual void visit(Integer &integer) = 0; virtual void visit(PositionalParameter &integer) = 0; virtual void visit(String &string) = 0; virtual void visit(Builtin &builtin) = 0; virtual void visit(Identifier &identifier) = 0; virtual void visit(StackMode &mode) = 0; virtual void visit(Call &call) = 0; virtual void visit(Map &map) = 0; virtual void visit(Variable &var) = 0; virtual void visit(Binop &binop) = 0; virtual void visit(Unop &unop) = 0; virtual void visit(Ternary &ternary) = 0; virtual void visit(FieldAccess &acc) = 0; virtual void visit(ArrayAccess &arr) = 0; virtual void visit(Cast &cast) = 0; virtual void visit(Tuple &tuple) = 0; virtual void visit(ExprStatement &expr) = 0; virtual void visit(AssignMapStatement &assignment) = 0; virtual void visit(AssignVarStatement &assignment) = 0; virtual void visit(If &if_block) = 0; virtual void visit(Jump &jump) = 0; virtual void visit(Unroll &unroll) = 0; virtual void visit(While &while_block) = 0; virtual void visit(Predicate &pred) = 0; virtual void visit(AttachPoint &ap) = 0; virtual void visit(Probe &probe) = 0; virtual void visit(Program &program) = 0; }; /** Basic tree walking visitor The Visit() method is called one for every node in the tree. Providing an easy way to run a generic method on all nodes. The individual visit() methods run on specific node types. */ class Visitor : public VisitorBase { public: explicit Visitor() = default; ~Visitor() = default; Visitor(const Visitor &) = delete; Visitor &operator=(const Visitor &) = delete; Visitor(Visitor &&) = delete; Visitor &operator=(Visitor &&) = delete; /* Visit a node */ virtual inline void Visit(Node &n) { n.accept(*this); }; /* Visitors for specific node types NB: visitor should dispatch through the Visit method and not use node->accept() directly */ void visit(Integer &integer) override; void visit(PositionalParameter ¶m) override; void visit(String &string) override; void visit(StackMode &mode) override; void visit(Identifier &identifier) override; void visit(Builtin &builtin) override; void visit(Call &call) override; void visit(Map &map) override; void visit(Variable &var) override; void visit(Binop &binop) override; void visit(Unop &unop) override; void visit(Ternary &ternary) override; void visit(FieldAccess &acc) override; void visit(ArrayAccess &arr) override; void visit(Cast &cast) override; void visit(Tuple &tuple) override; void visit(ExprStatement &expr) override; void visit(AssignMapStatement &assignment) override; void visit(AssignVarStatement &assignment) override; void visit(If &if_block) override; void visit(Unroll &unroll) override; void visit(While &while_block) override; void visit(Jump &jump) override; void visit(Predicate &pred) override; void visit(AttachPoint &ap) override; void visit(Probe &probe) override; void visit(Program &program) override; }; /** Base class for vtable based dispatching \tparam R return type for visitors */ template class Dispatcher { private: using tabletype = VTable; using mytype = Dispatcher; public: virtual ~Dispatcher() = default; /** Visit handles the dispatching on node type */ virtual R Visit(Node &node) { static tabletype table = make_vtable(); return table(node, this); }; #define DEFAULT_FN \ { \ return default_visitor(node); \ } /** Visitors for node subtypes */ virtual R visit(Integer &node) DEFAULT_FN; virtual R visit(PositionalParameter &node) DEFAULT_FN; virtual R visit(String &node) DEFAULT_FN; virtual R visit(Builtin &node) DEFAULT_FN; virtual R visit(Identifier &node) DEFAULT_FN; virtual R visit(StackMode &node) DEFAULT_FN; virtual R visit(Call &node) DEFAULT_FN; virtual R visit(Map &node) DEFAULT_FN; virtual R visit(Variable &node) DEFAULT_FN; virtual R visit(Binop &node) DEFAULT_FN; virtual R visit(Unop &node) DEFAULT_FN; virtual R visit(Ternary &node) DEFAULT_FN; virtual R visit(FieldAccess &node) DEFAULT_FN; virtual R visit(ArrayAccess &node) DEFAULT_FN; virtual R visit(Cast &node) DEFAULT_FN; virtual R visit(Tuple &node) DEFAULT_FN; virtual R visit(ExprStatement &node) DEFAULT_FN; virtual R visit(AssignMapStatement &node) DEFAULT_FN; virtual R visit(AssignVarStatement &node) DEFAULT_FN; virtual R visit(If &node) DEFAULT_FN; virtual R visit(Jump &node) DEFAULT_FN; virtual R visit(Unroll &node) DEFAULT_FN; virtual R visit(While &node) DEFAULT_FN; virtual R visit(Predicate &node) DEFAULT_FN; virtual R visit(AttachPoint &node) DEFAULT_FN; virtual R visit(Probe &node) DEFAULT_FN; virtual R visit(Program &node) DEFAULT_FN; virtual R default_visitor(Node &node) { throw std::runtime_error(std::string("No visitor for: ") + typeid(node).name()); } private: // Helper for easily defining vtable entries #define DEFINE_DISPATCH(T) \ { \ table.template set( \ [](Node &n, mytype *v) { return v->visit(static_cast(n)); }); \ } static tabletype make_vtable() { tabletype table; DEFINE_DISPATCH(Integer); DEFINE_DISPATCH(PositionalParameter); DEFINE_DISPATCH(String); DEFINE_DISPATCH(StackMode); DEFINE_DISPATCH(Identifier); DEFINE_DISPATCH(Builtin); DEFINE_DISPATCH(Call); DEFINE_DISPATCH(Map); DEFINE_DISPATCH(Variable); DEFINE_DISPATCH(Binop); DEFINE_DISPATCH(Unop); DEFINE_DISPATCH(FieldAccess); DEFINE_DISPATCH(ArrayAccess); DEFINE_DISPATCH(Cast); DEFINE_DISPATCH(Tuple); DEFINE_DISPATCH(ExprStatement); DEFINE_DISPATCH(AssignMapStatement); DEFINE_DISPATCH(AssignVarStatement); DEFINE_DISPATCH(If); DEFINE_DISPATCH(Unroll); DEFINE_DISPATCH(Jump); DEFINE_DISPATCH(Predicate); DEFINE_DISPATCH(Ternary); DEFINE_DISPATCH(While); DEFINE_DISPATCH(AttachPoint); DEFINE_DISPATCH(Probe); DEFINE_DISPATCH(Program); return table; } }; #undef DEFINE_DISPATCH #undef DEFAULT_FN /** Base for tree mutators Mutators are used to create a modified AST. While iterating the tree they construct a copy which will be returned at the end. This makes it possible to write optimizing passes. */ class Mutator : public Dispatcher { public: Mutator(){}; Node *visit(Integer &) override; Node *visit(PositionalParameter &) override; Node *visit(String &) override; Node *visit(Builtin &) override; Node *visit(Identifier &) override; Node *visit(StackMode &) override; Node *visit(Call &) override; Node *visit(Map &) override; Node *visit(Variable &) override; Node *visit(Binop &) override; Node *visit(Unop &) override; Node *visit(Ternary &) override; Node *visit(FieldAccess &) override; Node *visit(ArrayAccess &) override; Node *visit(Cast &) override; Node *visit(Tuple &) override; Node *visit(ExprStatement &) override; Node *visit(AssignMapStatement &) override; Node *visit(AssignVarStatement &) override; Node *visit(If &) override; Node *visit(Jump &) override; Node *visit(Unroll &) override; Node *visit(While &) override; Node *visit(Predicate &) override; Node *visit(AttachPoint &) override; Node *visit(Probe &) override; Node *visit(Program &) override; protected: // Value is a type casting wrapper for Visit() template T *Value(Node *n); /* visit each node in the list and return the modified list */ ExpressionList *mutateExprList(ExpressionList *src); StatementList *mutateStmtList(StatementList *src); }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/ast/vtable.h000066400000000000000000000041441413460502400163700ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace bpftrace { namespace ast { class Node; /** Dynamic dispatch implementation for AST Nodes This implement custom virtual dispatch for AST iteration. The benefit over double dispatch is that we can control the passed arguments and return value which is hard to do with double dispatch, a different Accept() would have to be implement for every new signature needed. Which makes tree modification harder to implement. The downside is that this way of dispatching is significantly slower. The core is a map of Types (using std::type_index) to FunctionPointers. A bit of templating makes it reusable for different types of arguments and return types. The current implementation only supports dispatching on Nodes and only support one argument, a Visitor. But this can by extended with some template magic. \code VTable namer; namer.set([](Node &n, MyVisitor *V){ return "int"; }; namer.set([](Node &n, MyVisitor *V){ return "string"; }; Node * i = Integer(); Node * s = String(); MyVisitor visitor; // unused std::cout << namer(i, visitor); // outputs: "int" std::cout << namer(s, visitor); // outputs: "string" \endcode \tparam R the return type \tparam D type to dispatch on \tparam V visitor type */ template class VTable { private: typedef R (*FuncPtr)(D &, V *); std::unordered_map vtable_; public: /** Set dispatch for type NT to function F */ template void set(FuncPtr F) { vtable_[std::type_index(typeid(NT))] = F; } /** Dispatch based on the type of n. If no entry for type n exists an exception is raised. */ R operator()(D &n, V *v) { auto itr = vtable_.find(std::type_index(typeid(n))); if (itr != vtable_.end()) return (*itr->second)(n, v); throw std::runtime_error(std::string("Unknown node: ") + typeid(n).name()); } }; } // namespace ast } // namespace bpftrace bpftrace-0.14.0/src/attached_probe.cpp000066400000000000000000001061341413460502400176250ustar00rootroot00000000000000#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 "attached_probe.h" #include "bpftrace.h" #include "disasm.h" #include "log.h" #include "probe_matcher.h" #include "usdt.h" #ifdef HAVE_LIBBPF_BPF_H #include #endif #include namespace libbpf { #undef __BPF_FUNC_MAPPER #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace { /* * Kernel functions that are unsafe to trace are excluded in the Kernel with * `notrace`. However, the ones below are not excluded. */ const std::set banned_kretprobes = { "_raw_spin_lock", "_raw_spin_lock_irqsave", "_raw_spin_unlock_irqrestore", "queued_spin_lock_slowpath", }; bpf_probe_attach_type attachtype(ProbeType t) { switch (t) { case ProbeType::kprobe: return BPF_PROBE_ENTRY; break; case ProbeType::kretprobe: return BPF_PROBE_RETURN; 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(FATAL) << "invalid probe attachtype \"" << probetypeName(t) << "\""; } // lgtm[cpp/missing-return] } bpf_prog_type progtype(ProbeType t) { switch (t) { case ProbeType::kprobe: return BPF_PROG_TYPE_KPROBE; break; case ProbeType::kretprobe: return BPF_PROG_TYPE_KPROBE; break; case ProbeType::uprobe: return BPF_PROG_TYPE_KPROBE; break; case ProbeType::uretprobe: return BPF_PROG_TYPE_KPROBE; break; case ProbeType::usdt: 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; case ProbeType::software: return BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::watchpoint: return BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::asyncwatchpoint: return BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::hardware: return BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::kfunc: return static_cast(libbpf::BPF_PROG_TYPE_TRACING); break; case ProbeType::kretfunc: return static_cast(libbpf::BPF_PROG_TYPE_TRACING); break; case ProbeType::iter: return static_cast(libbpf::BPF_PROG_TYPE_TRACING); break; case ProbeType::invalid: LOG(FATAL) << "program type invalid"; } return {}; // unreached } std::string progtypeName(bpf_prog_type t) { switch (static_cast(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(FATAL) << "invalid program type: " << t; } // lgtm[cpp/missing-return] } void check_banned_kretprobes(std::string const& kprobe_name) { if (banned_kretprobes.find(kprobe_name) != banned_kretprobes.end()) { LOG(ERROR) << "error: kretprobe:" << kprobe_name << " can't be used as it might lock up your system."; exit(1); } } #ifdef HAVE_BCC_KFUNC void AttachedProbe::attach_kfunc(void) { tracing_fd_ = bpf_attach_kfunc(progfd_); if (tracing_fd_ < 0) throw std::runtime_error("Error attaching probe: " + probe_.name); } int AttachedProbe::detach_kfunc(void) { close(tracing_fd_); return 0; } #else void AttachedProbe::attach_kfunc(void) { throw std::runtime_error( "Error attaching probe: " + probe_.name + ", kfunc not available for your linked against bcc version"); } int AttachedProbe::detach_kfunc(void) { LOG(ERROR) << "kfunc not available for linked against bcc version"; return -1; } #endif // HAVE_BCC_KFUNC #ifdef HAVE_LIBBPF_LINK_CREATE void AttachedProbe::attach_iter(void) { linkfd_ = bpf_link_create(progfd_, 0, static_cast( libbpf::BPF_TRACE_ITER), NULL); if (linkfd_ < 0) { throw std::runtime_error("Error attaching probe: '" + probe_.name + "'"); } } int AttachedProbe::detach_iter(void) { close(linkfd_); return 0; } #else void AttachedProbe::attach_iter(void) { throw std::runtime_error( "Error attaching probe: " + probe_.name + ", iter API is not available for linked libbpf version"); } int AttachedProbe::detach_iter(void) { LOG(ERROR) << "iter is not available for linked bpf version"; return 0; } #endif // HAVE_LIBBPF_LINK_CREATE AttachedProbe::AttachedProbe(Probe &probe, std::tuple func, bool safe_mode) : probe_(probe), func_(func) { load_prog(); if (bt_verbose) std::cerr << "Attaching " << probe_.name << std::endl; switch (probe_.type) { case ProbeType::kprobe: attach_kprobe(safe_mode); break; case ProbeType::kretprobe: check_banned_kretprobes(probe_.attach_point); attach_kprobe(safe_mode); break; case ProbeType::uprobe: case ProbeType::uretprobe: attach_uprobe(safe_mode); break; case ProbeType::tracepoint: attach_tracepoint(); break; case ProbeType::profile: attach_profile(); break; case ProbeType::interval: attach_interval(); break; case ProbeType::software: attach_software(); break; case ProbeType::hardware: attach_hardware(); break; case ProbeType::kfunc: case ProbeType::kretfunc: attach_kfunc(); break; case ProbeType::iter: attach_iter(); break; default: LOG(FATAL) << "invalid attached probe type \"" << probetypeName(probe_.type) << "\""; } } AttachedProbe::AttachedProbe(Probe &probe, std::tuple func, int pid, BPFfeature &feature) : probe_(probe), func_(func) { load_prog(); switch (probe_.type) { case ProbeType::usdt: attach_usdt(pid, feature); break; case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: attach_watchpoint(pid, probe.mode); break; default: LOG(FATAL) << "invalid attached probe type \"" << probetypeName(probe_.type) << "\""; } } AttachedProbe::~AttachedProbe() { int err = 0; for (int perf_event_fd : perf_event_fds_) { err = bpf_close_perf_event_fd(perf_event_fd); if (err) LOG(ERROR) << "failed to close perf event FDs for probe: " << probe_.name; } err = 0; switch (probe_.type) { case ProbeType::kprobe: case ProbeType::kretprobe: err = bpf_detach_kprobe(eventname().c_str()); break; case ProbeType::kfunc: case ProbeType::kretfunc: err = detach_kfunc(); break; case ProbeType::iter: err = detach_iter(); break; case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: if (usdt_destructor_) usdt_destructor_(); err = bpf_detach_uprobe(eventname().c_str()); break; case ProbeType::tracepoint: err = bpf_detach_tracepoint(probe_.path.c_str(), eventname().c_str()); break; case ProbeType::profile: case ProbeType::interval: case ProbeType::software: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::hardware: break; case ProbeType::invalid: LOG(FATAL) << "invalid attached probe type \"" << probetypeName(probe_.type) << "\" at destructor"; } if (err) LOG(ERROR) << "failed to detach probe: " << probe_.name; if (progfd_ >= 0) close(progfd_); } const Probe &AttachedProbe::probe() const { return probe_; } std::string AttachedProbe::eventprefix() const { switch (attachtype(probe_.type)) { case BPF_PROBE_ENTRY: return "p_"; case BPF_PROBE_RETURN: return "r_"; } return {}; // unreached } std::string AttachedProbe::eventname() const { std::ostringstream offset_str; std::string index_str = "_" + std::to_string(probe_.index); switch (probe_.type) { case ProbeType::kprobe: case ProbeType::kretprobe: offset_str << std::hex << offset_; return eventprefix() + sanitise(probe_.attach_point) + "_" + offset_str.str() + index_str; case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: offset_str << std::hex << offset_; return eventprefix() + sanitise(probe_.path) + "_" + offset_str.str() + index_str; case ProbeType::tracepoint: return probe_.attach_point; default: LOG(FATAL) << "invalid eventname probe \"" << probetypeName(probe_.type) << "\""; } } std::string AttachedProbe::sanitise(const std::string &str) { /* * Characters such as "." in event names are rejected by the kernel, * so sanitize: */ return std::regex_replace(str, std::regex("[^A-Za-z0-9_]"), "_"); } static int sym_name_cb(const char *symname, uint64_t start, uint64_t size, void *p) { struct symbol *sym = static_cast(p); if (sym->name == symname) { sym->start = start; sym->size = size; return -1; } return 0; } static int sym_address_cb(const char *symname, uint64_t start, uint64_t size, void *p) { struct symbol *sym = static_cast(p); if (sym->address >= start && sym->address < (start + size)) { sym->start = start; sym->size = size; sym->name = symname; return -1; } return 0; } static uint64_t resolve_offset(const std::string &path, const std::string &symbol, uint64_t loc) { bcc_symbol bcc_sym; if (bcc_resolve_symname(path.c_str(), symbol.c_str(), loc, 0, nullptr, &bcc_sym)) throw std::runtime_error("Could not resolve symbol: " + path + ":" + symbol); // Have to free sym.module, see: // https://github.com/iovisor/bcc/blob/ba73657cb8c4dab83dfb89eed4a8b3866255569a/src/cc/bcc_syms.h#L98-L99 if (bcc_sym.module) ::free(const_cast(bcc_sym.module)); return bcc_sym.offset; } static void check_alignment(std::string &path, std::string &symbol, uint64_t sym_offset, uint64_t func_offset, bool safe_mode, ProbeType type) { Disasm dasm(path); AlignState aligned = dasm.is_aligned(sym_offset, func_offset); std::string probe_name = probetypeName(type); std::string tmp = path + ":" + symbol + "+" + std::to_string(func_offset); // If we did not allow unaligned uprobes in the // compile time, force the safe mode now. #ifndef HAVE_UNSAFE_PROBE safe_mode = true; #endif switch (aligned) { case AlignState::Ok: return; case AlignState::NotAlign: if (safe_mode) throw std::runtime_error("Could not add " + probe_name + " into middle of instruction: " + tmp); else LOG(WARNING) << "Unsafe " + probe_name + " in the middle of the instruction: " << tmp; break; case AlignState::Fail: if (safe_mode) throw std::runtime_error("Failed to check if " + probe_name + " is in proper place: " + tmp); else LOG(WARNING) << "Unchecked " + probe_name + ": " << tmp; break; case AlignState::NotSupp: if (safe_mode) throw std::runtime_error("Can't check if " + probe_name + " is in proper place (compiled without " "(k|u)probe offset support): " + tmp); else LOG(WARNING) << "Unchecked " + probe_name + " : " << tmp; break; } } void AttachedProbe::resolve_offset_uprobe(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 = 0xffffffff; if (symbol.empty()) { sym.address = probe_.address; bcc_elf_foreach_sym(probe_.path.c_str(), sym_address_cb, &option, &sym); if (!sym.start) { if (safe_mode) { std::stringstream ss; ss << "0x" << std::hex << probe_.address; throw std::runtime_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!"; offset_ = probe_.address; return; } } symbol = sym.name; func_offset = probe_.address - sym.start; } else { sym.name = symbol; bcc_elf_foreach_sym(probe_.path.c_str(), sym_name_cb, &option, &sym); if (!sym.start) throw std::runtime_error("Could not resolve symbol: " + probe_.path + ":" + symbol); } if (probe_.type == ProbeType::uretprobe && func_offset != 0) { std::stringstream msg; msg << "uretprobes cannot be attached at function offset. " << "(address resolved to: " << symbol << "+" << func_offset << ")"; throw std::runtime_error(msg.str()); } if (func_offset >= sym.size) { std::stringstream ss; ss << sym.size; throw std::runtime_error("Offset outside the function bounds ('" + symbol + "' size is " + ss.str() + ")"); } uint64_t sym_offset = resolve_offset(probe_.path, probe_.attach_point, probe_.loc); offset_ = sym_offset + func_offset; // If we are not aligned to the start of the symbol, // check if we are on the instruction boundary. if (func_offset == 0) return; check_alignment( probe_.path, symbol, sym_offset, func_offset, safe_mode, probe_.type); } // find vmlinux file containing the given symbol information static std::string find_vmlinux(const struct vmlinux_location *locs, struct symbol &sym) { struct bcc_symbol_option option = {}; option.use_debug_file = 0; option.use_symbol_type = BCC_SYM_ALL_TYPES; struct utsname buf; uname(&buf); for (int i = 0; locs[i].path; i++) { if (locs[i].raw) continue; // This file is for BTF. skip char path[PATH_MAX + 1]; snprintf(path, PATH_MAX, locs[i].path, buf.release); if (access(path, R_OK)) continue; bcc_elf_foreach_sym(path, sym_name_cb, &option, &sym); if (sym.start) { if (bt_verbose) std::cout << "vmlinux: using " << path << std::endl; return path; } } return ""; } void AttachedProbe::resolve_offset_kprobe(bool safe_mode) { struct symbol sym = {}; std::string &symbol = probe_.attach_point; uint64_t func_offset = probe_.func_offset; offset_ = func_offset; #ifndef HAVE_UNSAFE_PROBE safe_mode = true; #endif if (func_offset == 0) return; sym.name = symbol; const struct vmlinux_location *locs = vmlinux_locs; struct vmlinux_location locs_env[] = { { nullptr, false }, { nullptr, false }, }; char *env_path = std::getenv("BPFTRACE_VMLINUX"); if (env_path) { locs_env[0].path = env_path; locs = locs_env; } std::string path = find_vmlinux(locs, sym); if (path.empty()) { if (safe_mode) { std::stringstream buf; buf << "Could not resolve symbol " << symbol << "."; buf << " Use BPFTRACE_VMLINUX env variable to specify vmlinux path."; #ifdef HAVE_UNSAFE_PROBE buf << " Use --unsafe to skip the userspace check."; #else buf << " Compile bpftrace with ALLOW_UNSAFE_PROBE option to force skip " "the check."; #endif throw std::runtime_error(buf.str()); } else { // linux kernel checks alignment, but not the function bounds if (bt_verbose) std::cout << "Could not resolve symbol " << symbol << ". Skip offset checking." << std::endl; return; } } if (func_offset >= sym.size) throw std::runtime_error("Offset outside the function bounds ('" + symbol + "' size is " + std::to_string(sym.size) + ")"); uint64_t sym_offset = resolve_offset(path, probe_.attach_point, probe_.loc); check_alignment( path, symbol, sym_offset, func_offset, safe_mode, probe_.type); } void AttachedProbe::load_prog() { uint8_t *insns = std::get<0>(func_); int prog_len = std::get<1>(func_); const char *license = "GPL"; int log_level = 0; uint64_t log_buf_size = probe_.log_size; auto log_buf = std::make_unique(log_buf_size); char name[STRING_SIZE]; const char *namep; std::string tracing_type, tracing_name; { // Redirect stderr, so we don't get error messages from BCC StderrSilencer silencer; if (bt_debug == DebugLevel::kNone) silencer.silence(); if (bt_debug != DebugLevel::kNone) log_level = 15; if (bt_verbose) log_level = 1; // bpf_prog_load rejects colons in the probe name strncpy(name, probe_.name.c_str(), STRING_SIZE - 1); namep = name; if (strrchr(name, ':') != NULL) namep = strrchr(name, ':') + 1; // The bcc_prog_load function now recognizes 'kfunc__/kretfunc__' // prefixes and detects and fills in all the necessary BTF related // attributes for loading the kfunc program. tracing_type = probetypeName(probe_.type); if (!tracing_type.empty()) { if (tracing_type == "iter") tracing_type = "bpf_iter"; tracing_name = tracing_type + "__" + namep; namep = tracing_name.c_str(); } for (int attempt = 0; attempt < 3; attempt++) { auto version = kernel_version(attempt); if (version == 0 && attempt > 0) { // Recent kernels don't check the version so we should try to call // bcc_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; } #ifdef HAVE_BCC_PROG_LOAD_XATTR struct bpf_load_program_attr attr = {}; attr.prog_type = progtype(probe_.type); attr.name = namep; attr.insns = reinterpret_cast(insns); attr.license = license; libbpf::bpf_prog_type prog_type = static_cast( progtype(probe_.type)); if (prog_type != libbpf::BPF_PROG_TYPE_TRACING && prog_type != libbpf::BPF_PROG_TYPE_EXT) attr.kern_version = version; attr.log_level = log_level; progfd_ = bcc_prog_load_xattr( &attr, prog_len, log_buf.get(), log_buf_size, true); #else // HAVE_BCC_PROG_LOAD_XATTR #ifdef HAVE_BCC_PROG_LOAD progfd_ = bcc_prog_load(progtype(probe_.type), namep, #else progfd_ = bpf_prog_load(progtype(probe_.type), namep, #endif reinterpret_cast(insns), prog_len, license, version, log_level, log_buf.get(), log_buf_size); #endif // HAVE_BCC_PROG_LOAD_XATTR if (progfd_ >= 0) break; } } if (progfd_ < 0) { if (bt_verbose) { std::cerr << std::endl << "Error log: " << std::endl << log_buf.get() << std::endl; if (errno == ENOSPC) { std::stringstream errmsg; errmsg << "Error: Failed to load program, verification log buffer " << "not big enough, try increasing the BPFTRACE_LOG_SIZE " << "environment variable beyond the current value of " << probe_.log_size << " bytes"; throw std::runtime_error(errmsg.str()); } } throw std::runtime_error("Error loading program: " + probe_.name + (bt_verbose ? "" : " (try -v)")); } if (bt_verbose) { struct bpf_prog_info info = {}; uint32_t info_len = sizeof(info); int ret; ret = bpf_obj_get_info(progfd_, &info, &info_len); if (ret == 0) { std::cout << std::endl << "Program ID: " << info.id << std::endl; } std::cout << std::endl << "The verifier log: " << std::endl << log_buf.get() << std::endl; } } void AttachedProbe::attach_kprobe(bool safe_mode) { resolve_offset_kprobe(safe_mode); #ifdef LIBBCC_ATTACH_KPROBE_SIX_ARGS_SIGNATURE int perf_event_fd = bpf_attach_kprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.attach_point.c_str(), offset_, 0); #else int perf_event_fd = bpf_attach_kprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.attach_point.c_str(), offset_); #endif if (perf_event_fd < 0) { if (probe_.orig_name != probe_.name) { // a wildcard expansion couldn't probe something, just print a warning // as this is normal for some kernel functions (eg, do_debug()) LOG(WARNING) << "could not attach probe " << probe_.name << ", skipping."; } else { // an explicit match failed, so fail as the user must have wanted it throw std::runtime_error("Error attaching probe: '" + probe_.name + "'"); } } perf_event_fds_.push_back(perf_event_fd); } void AttachedProbe::attach_uprobe(bool safe_mode) { resolve_offset_uprobe(safe_mode); int perf_event_fd = #ifdef LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE bpf_attach_uprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.path.c_str(), offset_, probe_.pid, 0); #else bpf_attach_uprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.path.c_str(), offset_, probe_.pid); #endif // LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE if (perf_event_fd < 0) throw std::runtime_error("Error attaching probe: " + probe_.name); perf_event_fds_.push_back(perf_event_fd); } int AttachedProbe::usdt_sem_up_manual(const std::string &fn_name, void *ctx) { int err; #ifdef BCC_USDT_HAS_FULLY_SPECIFIED_PROBE if (probe_.ns == "") err = bcc_usdt_enable_probe(ctx, probe_.attach_point.c_str(), fn_name.c_str()); else err = bcc_usdt_enable_fully_specified_probe( ctx, probe_.ns.c_str(), probe_.attach_point.c_str(), fn_name.c_str()); #else err = bcc_usdt_enable_probe(ctx, probe_.attach_point.c_str(), fn_name.c_str()); #endif // BCC_USDT_HAS_FULLY_SPECIFIED_PROBE // Defer context destruction until probes are detached b/c context // destruction will decrement usdt semaphore count. usdt_destructor_ = [ctx]() { bcc_usdt_close(ctx); }; return err; } #ifdef HAVE_BCC_USDT_ADDSEM int AttachedProbe::usdt_sem_up_manual_addsem(int pid, const std::string &fn_name, void *ctx) { // NB: we are careful to capture by value here everything that will not // be available in AttachedProbe destructor. auto addsem = [this, fn_name](void *c, int16_t val) -> int { if (this->probe_.ns == "") return bcc_usdt_addsem_probe( c, this->probe_.attach_point.c_str(), fn_name.c_str(), val); else return bcc_usdt_addsem_fully_specified_probe( c, this->probe_.ns.c_str(), this->probe_.attach_point.c_str(), fn_name.c_str(), val); }; // Set destructor to decrement the semaphore count usdt_destructor_ = [pid, addsem]() { void *c = bcc_usdt_new_frompid(pid, nullptr); if (!c) return; addsem(c, -1); bcc_usdt_close(c); }; // Use semaphore increment API to avoid having to hold onto the usdt context // for the entire tracing session. Reason we do it this way instead of // holding onto usdt context is b/c each usdt context can take lots of memory // (~10MB). This, coupled with --usdt-file-activation and tracees that have a // forking model can cause bpftrace to use huge amounts of memory if we hold // onto the contexts. int err = addsem(ctx, +1); // Now close the context to save some memory bcc_usdt_close(ctx); return err; } #else int AttachedProbe::usdt_sem_up_manual_addsem(int pid __attribute__((unused)), const std::string &fn_name __attribute__((unused)), void *ctx __attribute__((unused))) { return 0; } #endif // HAVE_BCC_USDT_ADDSEM int AttachedProbe::usdt_sem_up([[maybe_unused]] BPFfeature &feature, [[maybe_unused]] int pid, const std::string &fn_name, void *ctx) { // If we have BCC and kernel support for uprobe refcnt API, then we don't // need to do anything here. The kernel will increment the semaphore count // for us when we provide the semaphore offset. if (feature.has_uprobe_refcnt()) { bcc_usdt_close(ctx); return 0; } #if defined(HAVE_BCC_USDT_ADDSEM) return usdt_sem_up_manual_addsem(pid, fn_name, ctx); #else return usdt_sem_up_manual(fn_name, ctx); #endif } void AttachedProbe::attach_usdt(int pid, BPFfeature &feature) { struct bcc_usdt_location loc = {}; int err; void *ctx; // TODO: fn_name may need a unique suffix for each attachment on the same // probe: std::string fn_name = "probe_" + probe_.attach_point + "_1"; if (pid) { // FIXME when iovisor/bcc#2064 is merged, optionally pass probe_.path ctx = bcc_usdt_new_frompid(pid, nullptr); if (!ctx) throw std::runtime_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) throw std::runtime_error("Error initializing context for probe: " + probe_.name); } // Resolve location of usdt probe auto u = USDTHelper::find(pid, probe_.path, probe_.ns, probe_.attach_point); if (!u.has_value()) throw std::runtime_error("Failed to find usdt probe: " + eventname()); probe_.path = u->path; err = bcc_usdt_get_location(ctx, probe_.ns.c_str(), probe_.attach_point.c_str(), probe_.usdt_location_idx, &loc); if (err) throw std::runtime_error("Error finding location for probe: " + probe_.name); probe_.loc = loc.address; offset_ = resolve_offset(probe_.path, probe_.attach_point, probe_.loc); // Should be 0 if there's no semaphore // // Cast to 32 bits b/c kernel API only takes 32 bit offset [[maybe_unused]] auto semaphore_offset = static_cast( u->semaphore_offset); // Increment the semaphore count (will noop if no semaphore) // // NB: Do *not* use `ctx` after this call. It may either be open or closed, // depending on which path was taken. err = usdt_sem_up(feature, pid, fn_name, ctx); if (err) { std::string err; err += "Error finding or enabling probe: " + probe_.name; err += '\n'; err += "Try using -p or --usdt-file-activation if there's USDT semaphores"; throw std::runtime_error(err); } int perf_event_fd = #ifdef LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE bpf_attach_uprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.path.c_str(), offset_, pid == 0 ? -1 : pid, semaphore_offset); #else bpf_attach_uprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.path.c_str(), offset_, pid == 0 ? -1 : pid); #endif // LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE if (perf_event_fd < 0) { if (pid) throw std::runtime_error("Error attaching probe: " + probe_.name + ", to PID: " + std::to_string(pid)); else throw std::runtime_error("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } void AttachedProbe::attach_tracepoint() { int perf_event_fd = bpf_attach_tracepoint(progfd_, probe_.path.c_str(), eventname().c_str()); if (perf_event_fd < 0) throw std::runtime_error("Error attaching probe: " + probe_.name); perf_event_fds_.push_back(perf_event_fd); } void AttachedProbe::attach_profile() { int pid = -1; int group_fd = -1; uint64_t period, freq; if (probe_.path == "hz") { period = 0; freq = probe_.freq; } else if (probe_.path == "s") { period = probe_.freq * 1e9; freq = 0; } else if (probe_.path == "ms") { period = probe_.freq * 1e6; freq = 0; } else if (probe_.path == "us") { period = probe_.freq * 1e3; freq = 0; } else { LOG(FATAL) << "invalid profile path \"" << probe_.path << "\""; } std::vector cpus = get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = bpf_attach_perf_event(progfd_, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, period, freq, pid, cpu, group_fd); if (perf_event_fd < 0) throw std::runtime_error("Error attaching probe: " + probe_.name); perf_event_fds_.push_back(perf_event_fd); } } void AttachedProbe::attach_interval() { int pid = -1; int group_fd = -1; int cpu = 0; uint64_t period = 0, freq = 0; if (probe_.path == "s") { period = probe_.freq * 1e9; } else if (probe_.path == "ms") { period = probe_.freq * 1e6; } else if (probe_.path == "us") { period = probe_.freq * 1e3; } else if (probe_.path == "hz") { freq = probe_.freq; } else { LOG(FATAL) << "invalid interval path \"" << probe_.path << "\""; } int perf_event_fd = bpf_attach_perf_event(progfd_, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, period, freq, pid, cpu, group_fd); if (perf_event_fd < 0) throw std::runtime_error("Error attaching probe: " + probe_.name); perf_event_fds_.push_back(perf_event_fd); } void AttachedProbe::attach_software() { int pid = -1; int group_fd = -1; uint64_t period = probe_.freq; uint64_t defaultp = 1; uint32_t type = 0; // from linux/perf_event.h, with aliases from perf: for (auto &probeListItem : SW_PROBE_LIST) { if (probe_.path == probeListItem.path || probe_.path == probeListItem.alias) { type = probeListItem.type; defaultp = probeListItem.defaultp; } } if (period == 0) period = defaultp; std::vector cpus = get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = bpf_attach_perf_event(progfd_, PERF_TYPE_SOFTWARE, type, period, 0, pid, cpu, group_fd); if (perf_event_fd < 0) throw std::runtime_error("Error attaching probe: " + probe_.name); perf_event_fds_.push_back(perf_event_fd); } } void AttachedProbe::attach_hardware() { int pid = -1; int group_fd = -1; uint64_t period = probe_.freq; uint64_t defaultp = 1000000; uint32_t type = 0; // from linux/perf_event.h, with aliases from perf: for (auto &probeListItem : HW_PROBE_LIST) { if (probe_.path == probeListItem.path || probe_.path == probeListItem.alias) { type = probeListItem.type; defaultp = probeListItem.defaultp; } } if (period == 0) period = defaultp; std::vector cpus = get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = bpf_attach_perf_event(progfd_, PERF_TYPE_HARDWARE, type, period, 0, pid, cpu, group_fd); if (perf_event_fd < 0) throw std::runtime_error("Error attaching probe: " + probe_.name); perf_event_fds_.push_back(perf_event_fd); } } void AttachedProbe::attach_watchpoint(int pid, const std::string& mode) { struct perf_event_attr attr = {}; attr.type = PERF_TYPE_BREAKPOINT; attr.size = sizeof(struct perf_event_attr); attr.config = 0; attr.bp_type = HW_BREAKPOINT_EMPTY; for (const char c : mode) { if (c == 'r') attr.bp_type |= HW_BREAKPOINT_R; else if (c == 'w') attr.bp_type |= HW_BREAKPOINT_W; else if (c == 'x') attr.bp_type |= HW_BREAKPOINT_X; } attr.bp_addr = probe_.address; attr.bp_len = probe_.len; // Generate a notification every 1 event; we care about every event attr.sample_period = 1; std::vector cpus; if (pid >= 1) { cpus = { -1 }; } else { cpus = get_online_cpus(); pid = -1; } for (int cpu : cpus) { // We copy paste the code from bcc's bpf_attach_perf_event_raw here // because we need to know the exact error codes (and also we don't // want bcc's noisy error messages). int perf_event_fd = syscall( __NR_perf_event_open, &attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); if (perf_event_fd < 0) { if (errno == ENOSPC) throw EnospcException("No more HW registers left"); else throw std::system_error(errno, std::generic_category(), "Error attaching probe: " + probe_.name); } if (ioctl(perf_event_fd, PERF_EVENT_IOC_SET_BPF, progfd_) != 0) { close(perf_event_fd); throw std::system_error(errno, std::generic_category(), "Error attaching probe: " + probe_.name); } if (ioctl(perf_event_fd, PERF_EVENT_IOC_ENABLE, 0) != 0) { close(perf_event_fd); throw std::system_error(errno, std::generic_category(), "Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } } } // namespace bpftrace bpftrace-0.14.0/src/attached_probe.h000066400000000000000000000042671413460502400172760ustar00rootroot00000000000000#pragma once #include #include #include #include #include "bpffeature.h" #include "types.h" #include namespace bpftrace { bpf_probe_attach_type attachtype(ProbeType t); bpf_prog_type progtype(ProbeType t); std::string progtypeName(bpf_prog_type t); class AttachedProbe { public: AttachedProbe(Probe &probe, std::tuple func, bool safe_mode); AttachedProbe(Probe &probe, std::tuple func, int pid, BPFfeature &feature); ~AttachedProbe(); AttachedProbe(const AttachedProbe &) = delete; AttachedProbe &operator=(const AttachedProbe &) = delete; const Probe &probe() const; int linkfd_ = -1; private: std::string eventprefix() const; std::string eventname() const; static std::string sanitise(const std::string &str); void resolve_offset_kprobe(bool safe_mode); void resolve_offset_uprobe(bool safe_mode); void load_prog(); void attach_kprobe(bool safe_mode); void attach_uprobe(bool safe_mode); // Note: the following usdt attachment functions will only activate a // semaphore if one exists. // // Increment semaphore count manually with memory hogging API (least // preferrable) int usdt_sem_up_manual(const std::string &fn_name, void *ctx); // Increment semaphore count manually with BCC addsem API int usdt_sem_up_manual_addsem(int pid, const std::string &fn_name, void *ctx); int usdt_sem_up(BPFfeature &feature, int pid, const std::string &fn_name, void *ctx); void attach_usdt(int pid, BPFfeature &feature); void attach_tracepoint(); void attach_profile(); void attach_interval(); void attach_software(); void attach_hardware(); void attach_watchpoint(int pid, const std::string &mode); void attach_kfunc(void); int detach_kfunc(void); void attach_iter(void); int detach_iter(void); Probe &probe_; std::tuple func_; std::vector perf_event_fds_; int progfd_ = -1; uint64_t offset_ = 0; int tracing_fd_ = -1; std::function usdt_destructor_; }; } // namespace bpftrace bpftrace-0.14.0/src/bfd-disasm.cpp000066400000000000000000000046411413460502400166720ustar00rootroot00000000000000#include #include #include #include #include // bfd.h assumes everyone is using autotools and will error out unless // PACKAGE is defined. Some distros patch this check out. #define PACKAGE "bpftrace" #include "bfd-disasm.h" #include "utils.h" #include #include #include #include namespace bpftrace { BfdDisasm::BfdDisasm(std::string &path) : size_(0) { fd_ = open(path.c_str(), O_RDONLY); if (fd_ >= 0) { struct stat st; if (fstat(fd_, &st) == 0) size_ = st.st_size; } } BfdDisasm::~BfdDisasm() { if (fd_ >= 0) close(fd_); } static int fprintf_nop(void *out __attribute__((unused)), const char *fmt __attribute__((unused)), ...) { return 0; } static AlignState is_aligned_buf(void *buf, uint64_t size, uint64_t offset) { disassembler_ftype disassemble; struct disassemble_info info; std::string tpath = get_pid_exe("self"); bfd *bfdf; bfdf = bfd_openr(tpath.c_str(), nullptr); if (bfdf == nullptr) return AlignState::Fail; if (!bfd_check_format(bfdf, bfd_object)) { bfd_close(bfdf); return AlignState::Fail; } init_disassemble_info(&info, stdout, fprintf_nop); 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.14.0/src/bfd-disasm.h000066400000000000000000000004241413460502400163320ustar00rootroot00000000000000#pragma once #include "disasm.h" namespace bpftrace { class BfdDisasm : public IDisasm { public: BfdDisasm(std::string &path); ~BfdDisasm(); AlignState is_aligned(uint64_t offset, uint64_t pc); private: int fd_ = -1; uint64_t size_; }; } // namespace bpftrace bpftrace-0.14.0/src/bpffeature.cpp000066400000000000000000000246341413460502400170100ustar00rootroot00000000000000#include "bpffeature.h" #include #ifdef HAVE_LIBBPF_MAP_BATCH #include #endif #include #include #include #include #include #include #include "btf.h" #include "probe_matcher.h" #include "utils.h" namespace bpftrace { #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static bool try_load(const char* name, enum libbpf::bpf_prog_type prog_type, struct bpf_insn* insns, size_t insns_cnt, int loglevel, char* logbuf, size_t logbuf_size) { int ret = 0; StderrSilencer silencer; silencer.silence(); for (int attempt = 0; attempt < 3; attempt++) { auto version = kernel_version(attempt); if (version == 0 && attempt > 0) { // Recent kernels don't check the version so we should try to call // bcc_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; } #ifdef HAVE_BCC_PROG_LOAD ret = bcc_prog_load( #else ret = bpf_prog_load( #endif static_cast(prog_type), name, insns, insns_cnt * sizeof(struct bpf_insn), "GPL", version, loglevel, logbuf, logbuf_size); if (ret >= 0) { close(ret); return true; } } return false; } static bool try_load(enum libbpf::bpf_prog_type prog_type, struct bpf_insn* insns, size_t len, const char* name = nullptr) { constexpr int log_size = 4096; char logbuf[log_size] = {}; // kfunc / kretfunc only for now. We can refactor if more attach types // get added to BPF_PROG_TYPE_TRACING if (prog_type == libbpf::BPF_PROG_TYPE_TRACING && !name) { // List of available functions must be readable std::ifstream traceable_funcs(kprobe_path); // bcc checks the name (first arg) for the magic strings. If the bcc we // build against doesn't support kfunc then we will fail here. That's fine // because it still means kfunc doesn't work, only from a library side, not // a kernel side. return traceable_funcs.good() && try_load("kfunc__sched_fork", prog_type, insns, len, 0, logbuf, log_size) && try_load("kretfunc__sched_fork", prog_type, insns, len, 0, logbuf, log_size); } return try_load(name, prog_type, insns, len, 0, logbuf, log_size); } 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, 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); } bool BPFfeature::detect_prog_type(enum libbpf::bpf_prog_type prog_type, const char* name) { struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN() }; return try_load(prog_type, insns, ARRAY_SIZE(insns), name); } 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; default: break; } #ifdef HAVE_BCC_CREATE_MAP map_fd = bcc_create_map( #else map_fd = bpf_create_map( #endif static_cast(map_type), nullptr, key_size, value_size, max_entries, flags); if (map_fd >= 0) close(map_fd); return map_fd >= 0; } bool BPFfeature::has_loop(void) { if (has_loop_.has_value()) return *has_loop_; struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1), BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, -2), BPF_EXIT_INSN(), }; has_loop_ = std::make_optional( try_load(libbpf::BPF_PROG_TYPE_TRACEPOINT, insns, ARRAY_SIZE(insns))); return has_loop(); } bool BPFfeature::has_btf(void) { BTF btf; return btf.has_data(); } int BPFfeature::instruction_limit(void) { 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, 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() { #ifndef HAVE_LIBBPF_MAP_BATCH return false; #else 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_; #ifdef HAVE_BCC_CREATE_MAP map_fd = bcc_create_map( #else map_fd = bpf_create_map( #endif static_cast(libbpf::BPF_MAP_TYPE_HASH), nullptr, key_size, value_size, max_entries, flags); 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_; #endif } bool BPFfeature::has_d_path(void) { if (has_d_path_.has_value()) return *has_d_path_; struct bpf_insn insns[] = { BPF_LDX_MEM(BPF_W, 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), "kfunc__dentry_open")); return *has_d_path_; } bool BPFfeature::has_uprobe_refcnt() { if (has_uprobe_refcnt_.has_value()) return *has_uprobe_refcnt_; #ifdef LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE struct stat sb; has_uprobe_refcnt_ = ::stat("/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset", &sb) == 0; #else has_uprobe_refcnt_ = false; #endif // LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE return *has_uprobe_refcnt_; } std::string BPFfeature::report(void) { std::stringstream buf; auto to_str = [](bool f) -> auto { return f ? "yes\n" : "no\n"; }; buf << "Kernel helpers" << std::endl << " probe_read: " << to_str(has_helper_probe_read()) << " probe_read_str: " << to_str(has_helper_probe_read_str()) << " probe_read_user: " << to_str(has_helper_probe_read_user()) << " probe_read_user_str: " << to_str(has_helper_probe_read_user_str()) << " probe_read_kernel: " << to_str(has_helper_probe_read_kernel()) << " probe_read_kernel_str: " << to_str(has_helper_probe_read_kernel_str()) << " get_current_cgroup_id: " << to_str(has_helper_get_current_cgroup_id()) << " send_signal: " << to_str(has_helper_send_signal()) << " override_return: " << to_str(has_helper_override_return()) << " get_boot_ns: " << to_str(has_helper_ktime_get_boot_ns()) << " dpath: " << to_str(has_d_path()) << std::endl; buf << "Kernel features" << std::endl << " Instruction limit: " << instruction_limit() << std::endl << " Loop support: " << to_str(has_loop()) << " btf (depends on Build:libbpf): " << to_str(has_btf()) << " map batch (depends on Build:libbpf): " << to_str(has_map_batch()) << " uprobe refcount (depends on Build:bcc bpf_attach_uprobe refcount): " << to_str(has_uprobe_refcnt()) << std::endl; buf << "Map types" << std::endl << " hash: " << to_str(has_map_hash()) << " percpu hash: " << to_str(has_map_percpu_hash()) << " array: " << to_str(has_map_array()) << " percpu array: " << to_str(has_map_percpu_array()) << " stack_trace: " << to_str(has_map_stack_trace()) << " perf_event_array: " << to_str(has_map_perf_event_array()) << std::endl; buf << "Probe types" << std::endl << " kprobe: " << to_str(has_prog_kprobe()) << " tracepoint: " << to_str(has_prog_tracepoint()) << " perf_event: " << to_str(has_prog_perf_event()) << " kfunc: " << to_str(has_prog_kfunc()) << " iter:task: " << to_str(has_prog_iter_task()) << " iter:task_file: " << to_str(has_prog_iter_task_file()) << std::endl; return buf.str(); } } // namespace bpftrace bpftrace-0.14.0/src/bpffeature.h000066400000000000000000000135021413460502400164450ustar00rootroot00000000000000#pragma once #include #include #include #include namespace libbpf { #undef __BPF_FUNC_MAPPER #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) \ 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))); \ return *(prog_##var##_); \ } #define DEFINE_PROG_TEST(var, progtype) __DEFINE_PROG_TEST(var, progtype, NULL) #define DEFINE_PROG_TEST_FUNC(var, progtype, name) \ __DEFINE_PROG_TEST(var, progtype, name) class BPFfeature { public: BPFfeature() = default; virtual ~BPFfeature() = default; // Due to the unique_ptr usage the generated copy constructor & assignment // don't work. Move works but doesn't make sense as the `has_*` functions // will just reassign the unique_ptr. // A single bpffeature should be constructed in main() and passed around, // marking these as deleted to avoid accidentally copying/moving it. BPFfeature(const BPFfeature&) = delete; BPFfeature& operator=(const BPFfeature&) = delete; BPFfeature(BPFfeature&&) = delete; BPFfeature& operator=(BPFfeature&&) = delete; int instruction_limit(); bool has_loop(); bool has_btf(); bool has_map_batch(); bool has_d_path(); bool has_uprobe_refcnt(); std::string report(void); DEFINE_MAP_TEST(array, libbpf::BPF_MAP_TYPE_ARRAY); DEFINE_MAP_TEST(hash, libbpf::BPF_MAP_TYPE_HASH); DEFINE_MAP_TEST(percpu_array, libbpf::BPF_MAP_TYPE_PERCPU_ARRAY); DEFINE_MAP_TEST(percpu_hash, libbpf::BPF_MAP_TYPE_ARRAY); DEFINE_MAP_TEST(stack_trace, libbpf::BPF_MAP_TYPE_STACK_TRACE); DEFINE_MAP_TEST(perf_event_array, libbpf::BPF_MAP_TYPE_PERF_EVENT_ARRAY); DEFINE_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_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); DEFINE_PROG_TEST(kfunc, libbpf::BPF_PROG_TYPE_TRACING); DEFINE_PROG_TEST_FUNC(iter_task, libbpf::BPF_PROG_TYPE_TRACING, "bpf_iter__task"); DEFINE_PROG_TEST_FUNC(iter_task_file, libbpf::BPF_PROG_TYPE_TRACING, "bpf_iter__task_file"); protected: std::optional has_loop_; std::optional has_d_path_; std::optional insns_limit_; std::optional has_map_batch_; std::optional has_uprobe_refcnt_; private: bool detect_map(enum libbpf::bpf_map_type map_type); bool detect_helper(enum libbpf::bpf_func_id func_id, enum libbpf::bpf_prog_type prog_type); bool detect_prog_type(enum libbpf::bpf_prog_type prog_type, const char* name); }; #undef DEFINE_PROG_TEST #undef DEFINE_MAP_TEST #undef DEFINE_HELPER_TEST } // namespace bpftrace bpftrace-0.14.0/src/bpftrace.cpp000066400000000000000000002021541413460502400164460ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_BCC_ELF_FOREACH_SYM #include #include #endif #include #include #ifdef HAVE_LIBBPF_BPF_H #include #endif #include "ast/async_event_types.h" #include "bpftrace.h" #include "log.h" #include "printf.h" #include "relocator.h" #include "resolve_cgroupid.h" #include "triggers.h" #include "utils.h" namespace libbpf { #define __BPF_NAME_FN(x) #x const char *bpf_func_name[] = { __BPF_FUNC_MAPPER(__BPF_NAME_FN) }; #undef __BPF_NAME_FN } // namespace libbpf namespace bpftrace { DebugLevel bt_debug = DebugLevel::kNone; bool bt_quiet = false; bool bt_verbose = false; volatile sig_atomic_t BPFtrace::exitsig_recv = false; const int FMT_BUF_SZ = 512; std::string format(std::string fmt, std::vector> &args) { std::string retstr; auto buffer = std::vector(FMT_BUF_SZ); auto check_snprintf_ret = [](int r) { if (r < 0) { LOG(FATAL) << "format() error occurred: " << std::strerror(errno); } }; // Args have been made safe for printing by now, so replace nonstandard format // specifiers with %s size_t start_pos = 0; while ((start_pos = fmt.find("%r", start_pos)) != std::string::npos) { fmt.replace(start_pos, 2, "%s"); start_pos += 2; } auto tokens_begin = std::sregex_iterator(fmt.begin(), fmt.end(), format_specifier_re); auto tokens_end = std::sregex_iterator(); // replace format string tokens with args one by one int literal_text_pos = 0; // starting pos of literal text (text that is not // format specifier) int i = 0; // args index while (tokens_begin != tokens_end) { // take out the literal text retstr += fmt.substr(literal_text_pos, tokens_begin->position() - literal_text_pos); // replace current specifier with an arg int r = args.at(i)->print(buffer.data(), buffer.capacity(), tokens_begin->str().c_str()); check_snprintf_ret(r); if (static_cast(r) >= buffer.capacity()) { // the buffer is not big enough to hold the string, resize it buffer.resize(r + 1); int r = args.at(i)->print(buffer.data(), buffer.capacity(), tokens_begin->str().c_str()); check_snprintf_ret(r); } retstr += std::string(buffer.data()); // move to the next literal text literal_text_pos = tokens_begin->position() + tokens_begin->length(); ++tokens_begin; ++i; } // append whatever is left retstr += fmt.substr(literal_text_pos); return retstr; } BPFtrace::~BPFtrace() { for (const auto& pair : exe_sym_) { if (pair.second.second) bcc_free_symcache(pair.second.second, pair.second.first); } if (ksyms_) bcc_free_symcache(ksyms_, -1); } Probe BPFtrace::generateWatchpointSetupProbe(const std::string &func, const ast::AttachPoint &ap, const ast::Probe &probe) { Probe setup_probe; setup_probe.name = get_watchpoint_setup_probe_name(ap.name(func)); setup_probe.type = ProbeType::uprobe; setup_probe.path = ap.target; setup_probe.attach_point = func; setup_probe.orig_name = get_watchpoint_setup_probe_name(probe.name()); setup_probe.index = ap.index(func) > 0 ? ap.index(func) : probe.index(); return setup_probe; } int BPFtrace::add_probe(ast::Probe &p) { for (auto attach_point : *p.attach_points) { if (attach_point->provider == "BEGIN" || attach_point->provider == "END") { Probe probe; probe.path = "/proc/self/exe"; probe.attach_point = attach_point->provider + "_trigger"; probe.type = probetype(attach_point->provider); probe.log_size = log_size_; probe.orig_name = p.name(); probe.name = p.name(); probe.loc = 0; probe.index = attach_point->index(probe.name) > 0 ? attach_point->index(probe.name) : p.index(); resources.special_probes.push_back(probe); continue; } std::vector attach_funcs; // An underspecified usdt probe is a probe that has no wildcards and // either an empty namespace or a specified PID. // We try to find a unique match for such a probe. bool underspecified_usdt_probe = probetype(attach_point->provider) == ProbeType::usdt && !has_wildcard(attach_point->target) && !has_wildcard(attach_point->ns) && !has_wildcard(attach_point->func) && (attach_point->ns.empty() || pid() > 0); if (attach_point->need_expansion && (has_wildcard(attach_point->func) || has_wildcard(attach_point->target) || has_wildcard(attach_point->ns) || underspecified_usdt_probe)) { std::set matches; try { matches = probe_matcher_->get_matches_for_ap(*attach_point); } catch (const WildcardException &e) { LOG(ERROR) << e.what(); return 1; } if (underspecified_usdt_probe && matches.size() > 1) { LOG(ERROR) << "namespace for " << attach_point->name(attach_point->func) << " not specified, matched " << matches.size() << " probes"; LOG(INFO) << "please specify a unique namespace or use '*' to attach " << "to all matched probes"; return 1; } attach_funcs.insert(attach_funcs.end(), matches.begin(), matches.end()); } else if ((probetype(attach_point->provider) == ProbeType::uprobe || probetype(attach_point->provider) == ProbeType::uretprobe || probetype(attach_point->provider) == ProbeType::watchpoint || probetype(attach_point->provider) == ProbeType::asyncwatchpoint) && !attach_point->func.empty()) { std::set matches; struct symbol sym = {}; int err = resolve_uname(attach_point->func, &sym, attach_point->target); if (err < 0 || sym.address == 0) { // As the C++ language supports function overload, a given function name // (without parameters) could have multiple matches even when no // wildcards are used. matches = probe_matcher_->get_matches_for_ap(*attach_point); attach_funcs.insert(attach_funcs.end(), matches.begin(), matches.end()); } else { attach_funcs.push_back(attach_point->target + ":" + attach_point->func); } } else { if (probetype(attach_point->provider) == ProbeType::usdt && !attach_point->ns.empty()) attach_funcs.push_back(attach_point->target + ":" + attach_point->ns + ":" + attach_point->func); else if (probetype(attach_point->provider) == ProbeType::tracepoint || probetype(attach_point->provider) == ProbeType::uprobe || probetype(attach_point->provider) == ProbeType::uretprobe) attach_funcs.push_back(attach_point->target + ":" + attach_point->func); else attach_funcs.push_back(attach_point->func); } // You may notice that the below loop is somewhat duplicated in // codegen_llvm.cpp. The reason is because codegen tries to avoid // generating duplicate programs if it can be avoided. For example, a // program `kprobe:do_* { print("hi") }` can be generated once and reused // for multiple attachpoints. Thus, we need this loop here to attach the // single program to multiple attach points. // // There may be a way to refactor and unify the codepaths in a clean manner // but so far it has eluded your author. for (const auto &f : attach_funcs) { std::string func = f; std::string func_id = func; std::string target = attach_point->target; // USDT probes must specify a target binary path, a provider, and // a function name for full id. // So we will extract out the path and the provider namespace to get just // the function name if (probetype(attach_point->provider) == ProbeType::usdt ) { target = erase_prefix(func_id); std::string ns = erase_prefix(func_id); // Set attach_point target, ns, and func to their resolved values in // case of wildcards. attach_point->target = target; attach_point->ns = ns; attach_point->func = func_id; } else if (probetype(attach_point->provider) == ProbeType::tracepoint || probetype(attach_point->provider) == ProbeType::uprobe || probetype(attach_point->provider) == ProbeType::uretprobe) { // tracepoint and uprobe probes must specify both a target and // a function name. // We extract the target from func_id so that a resolved target and a // resolved function name are used in the probe. target = erase_prefix(func_id); } else if (probetype(attach_point->provider) == ProbeType::watchpoint || probetype(attach_point->provider) == ProbeType::asyncwatchpoint) { target = erase_prefix(func_id); erase_prefix(func); } else if (probetype(attach_point->provider) == ProbeType::iter) { has_iter_ = true; } Probe probe; probe.path = target; probe.attach_point = func_id; probe.type = probetype(attach_point->provider); probe.log_size = log_size_; probe.orig_name = p.name(); probe.ns = attach_point->ns; probe.name = attach_point->name(target, func_id); probe.freq = attach_point->freq; probe.address = attach_point->address; probe.func_offset = attach_point->func_offset; probe.loc = 0; probe.index = attach_point->index(func) > 0 ? attach_point->index(func) : p.index(); probe.len = attach_point->len; probe.mode = attach_point->mode; probe.async = attach_point->async; probe.pin = attach_point->pin; if (probetype(attach_point->provider) == ProbeType::usdt) { // We must attach to all locations of a USDT marker if duplicates exist // in a target binary. See comment in codegen_llvm.cpp probe generation // code for more details. for (int i = 0; i < attach_point->usdt.num_locations; ++i) { Probe probe_copy = probe; probe_copy.usdt_location_idx = i; probe_copy.index = attach_point->index(func + "_loc" + std::to_string(i)); resources.probes.emplace_back(std::move(probe_copy)); } } else if ((probetype(attach_point->provider) == ProbeType::watchpoint || probetype(attach_point->provider) == ProbeType::asyncwatchpoint) && attach_point->func.size()) { resources.probes.emplace_back( generateWatchpointSetupProbe(func_id, *attach_point, p)); resources.watchpoint_probes.emplace_back(std::move(probe)); } else { resources.probes.push_back(probe); } } } return 0; } int BPFtrace::num_probes() const { return resources.special_probes.size() + resources.probes.size(); } void BPFtrace::request_finalize() { finalize_ = true; attached_probes_.clear(); if (child_) child_->terminate(); } void perf_event_printer(void *cb_cookie, void *data, int size) { // The perf event data is not aligned, so we use memcpy to copy the data and // avoid UBSAN errors. Using an std::vector guarantees that it will be aligned // to the largest type. See: // https://stackoverflow.com/questions/8456236/how-is-a-vectors-data-aligned. std::vector data_aligned; data_aligned.resize(size); memcpy(data_aligned.data(), data, size); auto bpftrace = static_cast(cb_cookie); auto arg_data = data_aligned.data(); auto printf_id = *reinterpret_cast(arg_data); int err; // Ignore the remaining events if perf_event_printer is called during finalization // stage (exit() builtin has been called) if (bpftrace->finalize_) return; if (bpftrace->exitsig_recv) { bpftrace->request_finalize(); return; } // async actions if (printf_id == asyncactionint(AsyncAction::exit)) { bpftrace->request_finalize(); return; } else if (printf_id == asyncactionint(AsyncAction::print)) { auto print = static_cast(data); IMap *map = *bpftrace->maps[print->mapid]; err = bpftrace->print_map(*map, print->top, print->div); if (err) throw std::runtime_error("Could not print map with ident \"" + map->name_ + "\", err=" + std::to_string(err)); return; } else if (printf_id == asyncactionint(AsyncAction::print_non_map)) { auto print = static_cast(data); const SizedType &ty = bpftrace->resources.non_map_print_args.at( print->print_id); std::vector bytes; for (size_t i = 0; i < ty.GetSize(); ++i) bytes.emplace_back(reinterpret_cast(print->content[i])); bpftrace->out_->value(*bpftrace, ty, bytes); return; } else if (printf_id == asyncactionint(AsyncAction::clear)) { auto mapevent = static_cast(data); IMap *map = *bpftrace->maps[mapevent->mapid]; err = bpftrace->clear_map(*map); if (err) throw std::runtime_error("Could not clear map with ident \"" + map->name_ + "\", err=" + std::to_string(err)); return; } else if (printf_id == asyncactionint(AsyncAction::zero)) { auto mapevent = static_cast(data); IMap *map = *bpftrace->maps[mapevent->mapid]; err = bpftrace->zero_map(*map); if (err) throw std::runtime_error("Could not zero map with ident \"" + map->name_ + "\", err=" + std::to_string(err)); return; } else if (printf_id == asyncactionint(AsyncAction::time)) { char timestr[STRING_SIZE]; time_t t; struct tm tmp; t = time(NULL); if (!localtime_r(&t, &tmp)) { LOG(ERROR) << "localtime_r: " << strerror(errno); return; } auto time = static_cast(data); auto fmt = bpftrace->resources.time_args[time->time_id].c_str(); if (strftime(timestr, sizeof(timestr), fmt, &tmp) == 0) { LOG(ERROR) << "strftime returned 0"; return; } bpftrace->out_->message(MessageType::time, timestr, false); return; } else if (printf_id == asyncactionint(AsyncAction::join)) { uint64_t join_id = (uint64_t) * (static_cast(data) + 1); auto delim = bpftrace->resources.join_args[join_id].c_str(); std::stringstream joined; for (unsigned int i = 0; i < bpftrace->join_argnum_; i++) { auto *arg = arg_data + 2*sizeof(uint64_t) + i * bpftrace->join_argsize_; if (arg[0] == 0) break; if (i) joined << delim; joined << arg; } bpftrace->out_->message(MessageType::join, joined.str()); return; } else if (printf_id == asyncactionint(AsyncAction::helper_error)) { auto helpererror = static_cast(data); auto error_id = helpererror->error_id; auto return_value = helpererror->return_value; auto &info = bpftrace->resources.helper_error_info[error_id]; std::stringstream msg; msg << "Failed to " << libbpf::bpf_func_name[info.func_id] << ": "; if (return_value < 0) msg << strerror(-return_value) << " (" << return_value << ")"; else msg << return_value; LOG(WARNING, info.loc, std::cerr) << msg.str(); return; } else if (printf_id == asyncactionint(AsyncAction::watchpoint_attach)) { bool abort = false; auto watchpoint = static_cast(data); uint64_t probe_idx = watchpoint->watchpoint_idx; uint64_t addr = watchpoint->addr; if (probe_idx >= bpftrace->resources.watchpoint_probes.size()) { std::cerr << "Invalid watchpoint probe idx=" << probe_idx << std::endl; abort = true; goto out; } // Ignore duplicate watchpoints (idx && addr same), but allow the same // address to be watched by different probes. // // NB: this check works b/c we set Probe::addr below // // TODO: Should we be printing a warning or info message out here? if (bpftrace->resources.watchpoint_probes[probe_idx].address == addr) goto out; // Attach the real watchpoint probe { bool registers_available = true; Probe &wp_probe = bpftrace->resources.watchpoint_probes[probe_idx]; wp_probe.address = addr; std::vector> aps; try { aps = bpftrace->attach_probe(wp_probe, bpftrace->bytecode_); } catch (const EnospcException &ex) { registers_available = false; bpftrace->out_->message(MessageType::lost_events, "Failed to attach watchpoint probe. You are " "out of watchpoint registers."); goto out; } if (aps.empty() && registers_available) { std::cerr << "Unable to attach real watchpoint probe" << std::endl; abort = true; goto out; } for (auto &ap : aps) bpftrace->attached_probes_.emplace_back(std::move(ap)); } out: // Async watchpoints are not SIGSTOP'd if (bpftrace->resources.watchpoint_probes[probe_idx].async) return; // Let the tracee continue pid_t pid = bpftrace->child_ ? bpftrace->child_->pid() : (bpftrace->procmon_ ? bpftrace->procmon_->pid() : -1); if (pid == -1 || ::kill(pid, SIGCONT) != 0) { std::cerr << "Failed to SIGCONT tracee: " << strerror(errno) << std::endl; abort = true; } if (abort) std::abort(); return; } else if (printf_id == asyncactionint(AsyncAction::watchpoint_detach)) { auto unwatch = static_cast(data); uint64_t addr = unwatch->addr; // Remove all probes watching `addr`. Note how we fail silently here // (ie invalid addr). This lets script writers be a bit more aggressive // when unwatch'ing addresses, especially if they're sampling a portion // of addresses they're interested in watching. bpftrace->attached_probes_.erase( std::remove_if(bpftrace->attached_probes_.begin(), bpftrace->attached_probes_.end(), [&](const auto &ap) { return ap->probe().address == addr; }), bpftrace->attached_probes_.end()); return; } else if ( printf_id >= asyncactionint(AsyncAction::syscall) && printf_id < asyncactionint(AsyncAction::syscall) + RESERVED_IDS_PER_ASYNCACTION) { if (bpftrace->safe_mode_) { LOG(FATAL) << "syscall() not allowed in safe mode"; } auto id = printf_id - asyncactionint(AsyncAction::syscall); auto fmt = std::get<0>(bpftrace->resources.system_args[id]); auto args = std::get<1>(bpftrace->resources.system_args[id]); auto arg_values = bpftrace->get_arg_values(args, arg_data); bpftrace->out_->message(MessageType::syscall, exec_system(format(fmt, arg_values).c_str()), false); return; } else if ( printf_id >= asyncactionint(AsyncAction::cat)) { auto id = printf_id - asyncactionint(AsyncAction::cat); auto fmt = std::get<0>(bpftrace->resources.cat_args[id]); auto args = std::get<1>(bpftrace->resources.cat_args[id]); auto arg_values = bpftrace->get_arg_values(args, arg_data); std::stringstream buf; cat_file(format(fmt, arg_values).c_str(), bpftrace->cat_bytes_max_, buf); bpftrace->out_->message(MessageType::cat, buf.str(), false); return; } // printf auto fmt = std::get<0>(bpftrace->resources.printf_args[printf_id]); auto args = std::get<1>(bpftrace->resources.printf_args[printf_id]); auto arg_values = bpftrace->get_arg_values(args, arg_data); bpftrace->out_->message(MessageType::printf, format(fmt, arg_values), false); } std::vector> BPFtrace::get_arg_values(const std::vector &args, uint8_t* arg_data) { std::vector> arg_values; for (auto arg : args) { switch (arg.type.type) { case Type::integer: if (arg.type.IsSigned()) { int64_t val = 0; switch (arg.type.GetIntBitWidth()) { case 64: val = *reinterpret_cast(arg_data + arg.offset); break; case 32: val = *reinterpret_cast(arg_data + arg.offset); break; case 16: val = *reinterpret_cast(arg_data + arg.offset); break; case 8: val = *reinterpret_cast(arg_data + arg.offset); break; case 1: val = *reinterpret_cast(arg_data + arg.offset); break; default: LOG(FATAL) << "get_arg_values: invalid integer size. 8, 4, 2 and " "byte supported. " << arg.type.GetSize() << "provided"; } arg_values.push_back(std::make_unique(val)); } else { uint64_t val = 0; switch (arg.type.GetIntBitWidth()) { case 64: val = *reinterpret_cast(arg_data + arg.offset); break; case 32: val = *reinterpret_cast(arg_data + arg.offset); break; case 16: val = *reinterpret_cast(arg_data + arg.offset); break; case 8: val = *reinterpret_cast(arg_data + arg.offset); break; case 1: val = *reinterpret_cast(arg_data + arg.offset); break; default: LOG(FATAL) << "get_arg_values: invalid integer size. 8, 4, 2 and " "byte supported. " << arg.type.GetSize() << "provided"; } arg_values.push_back(std::make_unique(val)); } break; case Type::string: { auto p = reinterpret_cast(arg_data + arg.offset); arg_values.push_back(std::make_unique( std::string(p, strnlen(p, arg.type.GetSize())))); break; } case Type::buffer: arg_values.push_back(std::make_unique(resolve_buf( reinterpret_cast(arg_data + arg.offset)->content, reinterpret_cast(arg_data + arg.offset) ->length))); break; case Type::ksym: arg_values.push_back( std::make_unique( resolve_ksym(*reinterpret_cast(arg_data+arg.offset)))); break; case Type::usym: arg_values.push_back( std::make_unique( resolve_usym( *reinterpret_cast(arg_data+arg.offset), *reinterpret_cast(arg_data+arg.offset + 8)))); break; case Type::inet: arg_values.push_back( std::make_unique( resolve_inet( *reinterpret_cast(arg_data+arg.offset), reinterpret_cast(arg_data+arg.offset + 8)))); break; case Type::username: arg_values.push_back( std::make_unique( resolve_uid( *reinterpret_cast(arg_data+arg.offset)))); break; case Type::probe: arg_values.push_back( std::make_unique( resolve_probe( *reinterpret_cast(arg_data+arg.offset)))); break; case Type::kstack: arg_values.push_back( std::make_unique( get_stack( *reinterpret_cast(arg_data+arg.offset), false, arg.type.stack_type, 8))); break; case Type::ustack: arg_values.push_back( std::make_unique( get_stack( *reinterpret_cast(arg_data+arg.offset), true, arg.type.stack_type, 8))); break; case Type::timestamp: arg_values.push_back( std::make_unique(resolve_timestamp( reinterpret_cast(arg_data + arg.offset) ->strftime_id, reinterpret_cast(arg_data + arg.offset) ->nsecs_since_boot))); break; case Type::pointer: arg_values.push_back(std::make_unique( *reinterpret_cast(arg_data + arg.offset))); break; case Type::mac_address: arg_values.push_back( std::make_unique(resolve_mac_address( reinterpret_cast(arg_data + arg.offset)))); break; // fall through default: LOG(FATAL) << "invalid argument type"; } } return arg_values; } void BPFtrace::add_param(const std::string ¶m) { params_.emplace_back(param); } std::string BPFtrace::get_param(size_t i, bool is_str) const { if (params_.size() < i) { return is_str ? "" : "0"; } return params_.at(i-1); } size_t BPFtrace::num_params() const { return params_.size(); } void perf_event_lost(void *cb_cookie, uint64_t lost) { auto bpftrace = static_cast(cb_cookie); bpftrace->out_->lost_events(lost); } std::vector> BPFtrace::attach_usdt_probe( Probe &probe, std::tuple func, int pid, bool file_activation) { std::vector> ret; if (feature_->has_uprobe_refcnt() || !(file_activation && probe.path.size())) { ret.emplace_back( std::make_unique(probe, func, pid, *feature_)); return ret; } // File activation works by scanning through /proc/*/maps and seeing // which processes have the target executable in their address space // with execute permission. If found, we will try to attach to each // process we find. // // Note that this is the slow path. If the kernel has semaphore support // (feature_->has_uprobe_refcnt()), the kernel can do this for us and // much faster too. glob_t globbuf; if (::glob("/proc/[0-9]*/maps", GLOB_NOSORT, nullptr, &globbuf)) throw std::runtime_error("failed to glob"); char *p; if (!(p = realpath(probe.path.c_str(), nullptr))) { LOG(ERROR) << "Failed to resolve " << probe.path; return ret; } std::string resolved(p); free(p); for (size_t i = 0; i < globbuf.gl_pathc; ++i) { std::string path(globbuf.gl_pathv[i]); std::ifstream file(path); if (file.fail()) { // The process could have exited between the glob and now. We have // to silently ignore that. continue; } std::string line; while (std::getline(file, line)) { if (line.find(resolved) == std::string::npos) continue; auto parts = split_string(line, ' '); if (parts.at(1).find('x') == std::string::npos) continue; // Remove `/proc/` prefix std::string pid_str(globbuf.gl_pathv[i] + 6); // No need to remove `/maps` suffix b/c stoi() will ignore trailing !ints int pid_parsed; try { pid_parsed = std::stoi(pid_str); } catch (const std::exception &ex) { throw std::runtime_error("failed to parse pid=" + pid_str); } ret.emplace_back( std::make_unique(probe, func, pid_parsed, *feature_)); break; } } if (ret.empty()) LOG(ERROR) << "Failed to find processes running " << probe.path; return ret; } std::vector> BPFtrace::attach_probe( Probe &probe, BpfBytecode &bytecode) { std::vector> ret; auto get_section = [](BpfBytecode &bytecode, const std::string &name) -> std::optional> { auto sec = bytecode.find(name); if (sec == bytecode.end()) return std::nullopt; return std::make_tuple(sec->second.data(), sec->second.size()); }; // use the single-probe program if it exists (as is the case with wildcards // and the name builtin, which must be expanded into separate programs per // probe), else try to find a the program based on the original probe name // that includes wildcards. auto usdt_location_idx = (probe.type == ProbeType::usdt) ? std::make_optional( probe.usdt_location_idx) : std::nullopt; auto name = get_section_name_for_probe(probe.name, probe.index, usdt_location_idx); auto orig_name = get_section_name_for_probe(probe.orig_name, probe.index, usdt_location_idx); auto section = get_section(bytecode, name); if (!section) { section = get_section(bytecode, orig_name); } if (!section) { if (probe.name != probe.orig_name) LOG(ERROR) << "Code not generated for probe: " << probe.name << " from: " << probe.orig_name; else LOG(ERROR) << "Code not generated for probe: " << probe.name; return ret; } // Make a copy of the bytecode and perform relocations // // We choose not to modify the original bytecode to void keeping // track of state when the same bytecode is attached to multiple probes. std::vector relocated; relocated.reserve(std::get<1>(*section)); memcpy(relocated.data(), std::get<0>(*section), std::get<1>(*section)); std::get<0>(*section) = relocated.data(); auto relocator = Relocator(*section, *this); if (relocator.relocate()) { LOG(ERROR) << "Failed to relocate insns for probe: " << probe.name; return ret; } try { pid_t pid = child_ ? child_->pid() : this->pid(); if (probe.type == ProbeType::usdt) { auto aps = attach_usdt_probe(probe, *section, pid, usdt_file_activation_); for (auto &ap : aps) ret.emplace_back(std::move(ap)); return ret; } else if (probe.type == ProbeType::watchpoint || probe.type == ProbeType::asyncwatchpoint) { ret.emplace_back( std::make_unique(probe, *section, pid, *feature_)); return ret; } else { ret.emplace_back( std::make_unique(probe, *section, safe_mode_)); return ret; } } catch (const EnospcException &e) { // Caller will handle throw e; } catch (const std::runtime_error &e) { LOG(ERROR) << e.what(); ret.clear(); } return ret; } bool attach_reverse(const Probe &p) { switch(p.type) { case ProbeType::kprobe: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::software: case ProbeType::kfunc: case ProbeType::iter: return true; case ProbeType::kretfunc: case ProbeType::kretprobe: case ProbeType::tracepoint: case ProbeType::profile: case ProbeType::interval: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::hardware: return false; case ProbeType::invalid: LOG(FATAL) << "Unknown probe type"; } return {}; // unreached } int BPFtrace::run_special_probe(std::string name, BpfBytecode &bytecode, void (*trigger)(void)) { for (auto probe = resources.special_probes.rbegin(); probe != resources.special_probes.rend(); ++probe) { if ((*probe).attach_point == name) { probe->pid = getpid(); auto aps = attach_probe(*probe, bytecode); trigger(); return aps.size() ? 0 : -1; } } return 0; } #ifdef HAVE_LIBBPF_LINK_CREATE int BPFtrace::run_iter() { auto probe = resources.probes.begin(); char buf[1024] = {}; ssize_t len; if (probe == resources.probes.end()) { LOG(ERROR) << "Failed to create iter probe"; return 1; } // If a script contains an iter probe, it must be the only probe assert(attached_probes_.size() == 1); if (attached_probes_.empty()) { LOG(ERROR) << "Failed to attach iter probe"; return 1; } auto &ap = *attached_probes_.begin(); int link_fd = ap->linkfd_; if (link_fd < 0) { LOG(ERROR) << "Failed to link iter probe"; return 1; } if (probe->pin.empty()) { int iter_fd = bpf_iter_create(link_fd); if (iter_fd < 0) { LOG(ERROR) << "Failed to open iter probe link"; return 1; } while ((len = read(iter_fd, buf, sizeof(buf))) > 0) { fwrite(buf, len, 1, stdout); } close(iter_fd); } else { auto pin = probe->pin; if (pin.at(0) != '/') pin = "/sys/fs/bpf/" + pin; if (bpf_obj_pin(link_fd, pin.c_str())) LOG(ERROR) << "Failed to pin iter probe link"; else std::cout << "Program pinned to " << pin << std::endl; } return 0; } #else int BPFtrace::run_iter() { LOG(ERROR) << "iter is not available for linked bpf version"; return 1; } #endif int BPFtrace::prerun() const { uint64_t num_probes = this->num_probes(); if (num_probes == 0) { if (!bt_quiet) std::cout << "No probes to attach" << std::endl; return 1; } else if (num_probes > max_probes_) { LOG(ERROR) << "Can't attach to " << num_probes << " probes because it " << "exceeds the current limit of " << max_probes_ << " probes.\n" << "You can increase the limit through the BPFTRACE_MAX_PROBES " << "environment variable, but BE CAREFUL since a high number of probes " << "attached can cause your system to crash."; return 1; } else if (!bt_quiet) out_->attached_probes(num_probes); return 0; } int BPFtrace::run(BpfBytecode bytecode) { int err = prerun(); if (err) return err; // Clear fake maps and replace with real maps maps = {}; if (resources.create_maps(*this, false)) return 1; bytecode_ = std::move(bytecode); int epollfd = setup_perf_events(); if (epollfd < 0) return epollfd; if (maps.Has(MapManager::Type::Elapsed)) { struct timespec ts; clock_gettime(CLOCK_BOOTTIME, &ts); auto nsec = 1000000000ULL * ts.tv_sec + ts.tv_nsec; uint64_t key = 0; if (bpf_update_elem(maps[MapManager::Type::Elapsed].value()->mapfd_, &key, &nsec, 0) < 0) { perror("Failed to write start time to elapsed map"); return -1; } } if (run_special_probe("BEGIN_trigger", bytecode_, BEGIN_trigger)) return -1; if (child_ && has_usdt_) { try { child_->run(true); } catch (std::runtime_error &e) { LOG(ERROR) << "Failed to setup child: " << e.what(); return -1; } } // The kernel appears to fire some probes in the order that they were // attached and others in reverse order. In order to make sure that blocks // are executed in the same order they were declared, iterate over the probes // twice: in the first pass iterate forward and attach the probes that will // be fired in the same order they were attached, and in the second pass // iterate in reverse and attach the rest. for (auto probes = resources.probes.begin(); probes != resources.probes.end(); ++probes) { if (!attach_reverse(*probes)) { auto aps = attach_probe(*probes, bytecode_); if (aps.empty()) return -1; for (auto &ap : aps) attached_probes_.emplace_back(std::move(ap)); } } for (auto r_probes = resources.probes.rbegin(); r_probes != resources.probes.rend(); ++r_probes) { if (attach_reverse(*r_probes)) { auto aps = attach_probe(*r_probes, bytecode_); if (aps.empty()) return -1; for (auto &ap : aps) attached_probes_.emplace_back(std::move(ap)); } } // Kick the child to execute the command. if (child_) { try { if (has_usdt_) child_->resume(); else child_->run(); } catch (std::runtime_error &e) { LOG(ERROR) << "Failed to run child: " << e.what(); return -1; } } // Used by runtime test framework to know when to run AFTER directive if (std::getenv("__BPFTRACE_NOTIFY_PROBES_ATTACHED")) std::cerr << "__BPFTRACE_NOTIFY_PROBES_ATTACHED" << std::endl; if (has_iter_) { int err = run_iter(); if (err) return err; } else { poll_perf_events(epollfd); } attached_probes_.clear(); // finalize_ and exitsig_recv should be false from now on otherwise // perf_event_printer() can ignore the END_trigger() events. finalize_ = false; exitsig_recv = false; if (run_special_probe("END_trigger", bytecode_, END_trigger)) return -1; poll_perf_events(epollfd, true); // Calls perf_reader_free() on all open perf buffers. open_perf_buffers_.clear(); return 0; } int BPFtrace::setup_perf_events() { int epollfd = epoll_create1(EPOLL_CLOEXEC); if (epollfd == -1) { LOG(ERROR) << "Failed to create epollfd"; return -1; } std::vector cpus = get_online_cpus(); online_cpus_ = cpus.size(); for (int cpu : cpus) { void *reader = bpf_open_perf_buffer( &perf_event_printer, &perf_event_lost, this, -1, cpu, 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((perf_reader*)reader); bpf_update_elem( maps[MapManager::Type::PerfEvent].value()->mapfd_, &cpu, &reader_fd, 0); if (epoll_ctl(epollfd, EPOLL_CTL_ADD, reader_fd, &ev) == -1) { LOG(ERROR) << "Failed to add perf reader to epoll"; return -1; } } return epollfd; } void BPFtrace::poll_perf_events(int epollfd, bool drain) { auto events = std::vector(online_cpus_); while (true) { int ready = epoll_wait(epollfd, events.data(), online_cpus_, 100); if (ready < 0 && errno == EINTR && !BPFtrace::exitsig_recv) { // We received an interrupt not caused by SIGINT, skip and run again continue; } // Return if either // * 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. if (ready < 0 || (ready == 0 && (drain || finalize_))) { return; } for (int i=0; iis_alive()) || (child_ && !child_->is_alive())) { return; } } return; } int BPFtrace::print_maps() { for (auto &mapmap : maps) { if (!mapmap->is_printable()) continue; int err = print_map(*mapmap.get(), 0, 0); if (err) return err; } return 0; } // clear a map int BPFtrace::clear_map(IMap &map) { if (!map.is_clearable()) return zero_map(map); std::vector old_key; try { if (map.type_.IsHistTy() || map.type_.IsLhistTy() || map.type_.IsStatsTy() || map.type_.IsAvgTy()) // hist maps have 8 extra bytes for the bucket number old_key = find_empty_key(map, map.key_.size() + 8); else old_key = find_empty_key(map, map.key_.size()); } catch (std::runtime_error &e) { LOG(ERROR) << "failed to get key for map '" << map.name_ << "': " << e.what(); return -2; } auto key(old_key); // snapshot keys, then operate on them std::vector> keys; while (bpf_get_next_key(map.mapfd_, old_key.data(), key.data()) == 0) { keys.push_back(key); old_key = key; } for (auto &key : keys) { int err = bpf_delete_elem(map.mapfd_, key.data()); if (err) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } } return 0; } // zero a map int BPFtrace::zero_map(IMap &map) { uint32_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; std::vector old_key; try { if (map.type_.IsHistTy() || map.type_.IsLhistTy() || map.type_.IsStatsTy() || map.type_.IsAvgTy()) // hist maps have 8 extra bytes for the bucket number old_key = find_empty_key(map, map.key_.size() + 8); else old_key = find_empty_key(map, map.key_.size()); } catch (std::runtime_error &e) { LOG(ERROR) << "failed to get key for map '" << map.name_ << "': " << e.what(); return -2; } auto key(old_key); // snapshot keys, then operate on them std::vector> keys; while (bpf_get_next_key(map.mapfd_, old_key.data(), key.data()) == 0) { keys.push_back(key); old_key = key; } int value_size = map.type_.GetSize() * nvalues; std::vector zero(value_size, 0); for (auto &key : keys) { int err = bpf_update_elem(map.mapfd_, key.data(), zero.data(), BPF_EXIST); if (err) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } } return 0; } int BPFtrace::print_map(IMap &map, uint32_t top, uint32_t div) { if (map.type_.IsHistTy() || map.type_.IsLhistTy()) return print_map_hist(map, top, div); else if (map.type_.IsAvgTy() || map.type_.IsStatsTy()) return print_map_stats(map, top, div); uint32_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; std::vector old_key; try { old_key = find_empty_key(map, map.key_.size()); } catch (std::runtime_error &e) { LOG(ERROR) << "failed to get key for map '" << map.name_ << "': " << e.what(); return -2; } auto key(old_key); std::vector, std::vector>> values_by_key; while (bpf_get_next_key(map.mapfd_, old_key.data(), key.data()) == 0) { int value_size = map.type_.GetSize(); value_size *= nvalues; auto value = std::vector(value_size); int err = bpf_lookup_elem(map.mapfd_, key.data(), value.data()); if (err == -1) { // key was removed by the eBPF program during bpf_get_next_key() and bpf_lookup_elem(), // let's skip this key continue; } else if (err) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } values_by_key.push_back({key, value}); old_key = key; } if (map.type_.IsCountTy() || map.type_.IsSumTy() || map.type_.IsIntTy()) { bool is_signed = map.type_.IsSigned(); std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { if (is_signed) return reduce_value(a.second, nvalues) < reduce_value(b.second, nvalues); return reduce_value(a.second, nvalues) < reduce_value(b.second, nvalues); }); } else if (map.type_.IsMinTy()) { std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return min_value(a.second, nvalues) < min_value(b.second, nvalues); }); } else if (map.type_.IsMaxTy()) { std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return max_value(a.second, nvalues) < max_value(b.second, nvalues); }); } else { sort_by_key(map.key_.args_, values_by_key); }; if (div == 0) div = 1; out_->map(*this, map, top, div, values_by_key); return 0; } int BPFtrace::print_map_hist(IMap &map, uint32_t top, uint32_t div) { // A hist-map adds an extra 8 bytes onto the end of its key for storing // the bucket number. // e.g. A map defined as: @x[1, 2] = @hist(3); // would actually be stored with the key: [1, 2, 3] uint32_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; std::vector old_key; try { old_key = find_empty_key(map, map.key_.size() + 8); } catch (std::runtime_error &e) { LOG(ERROR) << "failed to get key for map '" << map.name_ << "': " << e.what(); return -2; } auto key(old_key); std::map, std::vector> values_by_key; while (bpf_get_next_key(map.mapfd_, old_key.data(), key.data()) == 0) { auto key_prefix = std::vector(map.key_.size()); uint64_t bucket = read_data(key.data() + map.key_.size()); for (size_t i=0; i(value_size); int err = bpf_lookup_elem(map.mapfd_, key.data(), value.data()); if (err == -1) { // key was removed by the eBPF program during bpf_get_next_key() and bpf_lookup_elem(), // let's skip this key continue; } else if (err) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } if (values_by_key.find(key_prefix) == values_by_key.end()) { // New key - create a list of buckets for it if (map.type_.IsHistTy()) values_by_key[key_prefix] = std::vector(65); else values_by_key[key_prefix] = std::vector(1002); } values_by_key[key_prefix].at(bucket) = reduce_value(value, nvalues); old_key = key; } // Sort based on sum of counts in all buckets std::vector, uint64_t>> total_counts_by_key; for (auto &map_elem : values_by_key) { int64_t sum = 0; for (size_t i=0; imap_hist(*this, map, top, div, values_by_key, total_counts_by_key); return 0; } int BPFtrace::print_map_stats(IMap &map, uint32_t top, uint32_t div) { uint32_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; // stats() and avg() maps add an extra 8 bytes onto the end of their key for // storing the bucket number. std::vector old_key; try { old_key = find_empty_key(map, map.key_.size() + 8); } catch (std::runtime_error &e) { LOG(ERROR) << "failed to get key for map '" << map.name_ << "': " << e.what(); return -2; } auto key(old_key); std::map, std::vector> values_by_key; while (bpf_get_next_key(map.mapfd_, old_key.data(), key.data()) == 0) { auto key_prefix = std::vector(map.key_.size()); uint64_t bucket = read_data(key.data() + map.key_.size()); for (size_t i=0; i(value_size); int err = bpf_lookup_elem(map.mapfd_, key.data(), value.data()); if (err == -1) { // key was removed by the eBPF program during bpf_get_next_key() and bpf_lookup_elem(), // let's skip this key continue; } else if (err) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } if (values_by_key.find(key_prefix) == values_by_key.end()) { // New key - create a list of buckets for it values_by_key[key_prefix] = std::vector(2); } values_by_key[key_prefix].at(bucket) = reduce_value(value, nvalues); old_key = key; } // Sort based on sum of counts in all buckets std::vector, int64_t>> total_counts_by_key; for (auto &map_elem : values_by_key) { assert(map_elem.second.size() == 2); int64_t count = map_elem.second.at(0); int64_t total = map_elem.second.at(1); int64_t value = 0; if (count != 0) value = total / count; total_counts_by_key.push_back({map_elem.first, value}); } std::sort(total_counts_by_key.begin(), total_counts_by_key.end(), [&](auto &a, auto &b) { return a.second < b.second; }); if (div == 0) div = 1; out_->map_stats(*this, map, top, div, values_by_key, total_counts_by_key); return 0; } std::optional BPFtrace::get_watchpoint_binary_path() const { if (child_) { // We can ignore all error checking here b/c child.cpp:validate_cmd() has // already done it auto args = split_string(cmd_, ' ', /* remove_empty= */ true); assert(!args.empty()); return resolve_binary_path(args[0]).front(); } else if (pid()) return "/proc/" + std::to_string(pid()) + "/exe"; else { return std::nullopt; } } std::vector BPFtrace::find_empty_key(IMap &map, size_t size) const { // 4.12 and above kernel supports passing NULL to BPF_MAP_GET_NEXT_KEY // to get first key of the map. For older kernels, the call will fail. if (size == 0) size = 8; auto key = std::vector(size); uint32_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; int value_size = map.type_.GetSize() * nvalues; auto value = std::vector(value_size); if (bpf_lookup_elem(map.mapfd_, key.data(), value.data())) return key; for (auto &elem : key) elem = 0xff; if (bpf_lookup_elem(map.mapfd_, key.data(), value.data())) return key; for (auto &elem : key) elem = 0x55; if (bpf_lookup_elem(map.mapfd_, key.data(), value.data())) return key; throw std::runtime_error("Could not find empty key"); } std::string BPFtrace::get_stack(uint64_t stackidpid, bool ustack, StackType stack_type, int indent) { int32_t stackid = stackidpid & 0xffffffff; int pid = stackidpid >> 32; auto stack_trace = std::vector(stack_type.limit); int err = bpf_lookup_elem(maps[stack_type].value()->mapfd_, &stackid, stack_trace.data()); if (err) { // ignore EFAULT errors: eg, kstack used but no kernel stack if (stackid != -EFAULT) LOG(ERROR) << "failed to look up stack id " << stackid << " (pid " << pid << "): " << err; return ""; } std::ostringstream stack; std::string padding(indent, ' '); stack << "\n"; for (auto &addr : stack_trace) { if (addr == 0) break; std::string sym; if (!ustack) sym = resolve_ksym(addr, true); else sym = resolve_usym(addr, pid, true, stack_type.mode == StackMode::perf); switch (stack_type.mode) { case StackMode::bpftrace: stack << padding << sym << std::endl; break; case StackMode::perf: stack << "\t" << std::hex << addr << std::dec << " " << sym << std::endl; break; } } return stack.str(); } std::string BPFtrace::resolve_uid(uintptr_t addr) const { std::string file_name = "/etc/passwd"; std::string uid = std::to_string(addr); std::string username = ""; std::ifstream file(file_name); if (file.fail()) { LOG(ERROR) << strerror(errno) << ": " << file_name; return username; } std::string line; bool found = false; while (std::getline(file, line) && !found) { auto fields = split_string(line, ':'); if (fields[2] == uid) { found = true; username = fields[0]; } } file.close(); return username; } std::string BPFtrace::resolve_timestamp(uint32_t strftime_id, uint64_t nsecs_since_boot) { static const auto usec_regex = std::regex("%f"); if (!boottime_) { LOG(ERROR) << "Cannot resolve timestamp due to failed boot time calcuation"; return "(?)"; } // Calculate and localize timestamp struct tm tmp; time_t time = boottime_->tv_sec + ((boottime_->tv_nsec + nsecs_since_boot) / 1e9); if (!localtime_r(&time, &tmp)) { LOG(ERROR) << "localtime_r: " << strerror(errno); return "(?)"; } // Process strftime() format string extensions const auto &raw_fmt = resources.strftime_args[strftime_id]; uint64_t us = ((boottime_->tv_nsec + nsecs_since_boot) % 1000000000) / 1000; char usecs_buf[7]; snprintf(usecs_buf, sizeof(usecs_buf), "%06lu", us); auto fmt = std::regex_replace(raw_fmt, usec_regex, usecs_buf); char timestr[STRING_SIZE]; if (strftime(timestr, sizeof(timestr), fmt.c_str(), &tmp) == 0) { LOG(ERROR) << "strftime returned 0"; return "(?)"; } return timestr; } std::string BPFtrace::resolve_buf(char *buf, size_t size) { return hex_format_buffer(buf, size); } std::string BPFtrace::resolve_ksym(uintptr_t addr, bool show_offset) { struct bcc_symbol ksym; std::ostringstream symbol; if (!ksyms_) ksyms_ = bcc_symcache_new(-1, nullptr); if (bcc_symcache_resolve(ksyms_, addr, &ksym) == 0) { symbol << ksym.name; if (show_offset) symbol << "+" << ksym.offset; } else { symbol << (void*)addr; } return symbol.str(); } uint64_t BPFtrace::resolve_kname(const std::string &name) const { uint64_t addr = 0; std::string file_name = "/proc/kallsyms"; std::ifstream file(file_name); if (file.fail()) { LOG(ERROR) << strerror(errno) << ": " << file_name; return addr; } std::string line; while (std::getline(file, line) && addr == 0) { auto tokens = split_string(line, ' '); if (name == tokens[2]) { addr = read_address_from_output(line); break; } } file.close(); return addr; } uint64_t BPFtrace::resolve_cgroupid(const std::string &path) const { return bpftrace_linux::resolve_cgroupid(path); } #ifdef HAVE_BCC_ELF_FOREACH_SYM static int sym_resolve_callback(const char *name, uint64_t addr, uint64_t size, void *payload) { struct symbol *sym = (struct symbol *)payload; if (!strcmp(name, sym->name.c_str())) { sym->address = addr; sym->size = size; return -1; } return 0; } #endif int BPFtrace::resolve_uname(const std::string &name, struct symbol *sym, const std::string &path) const { sym->name = name; #ifdef HAVE_BCC_ELF_FOREACH_SYM struct bcc_symbol_option option; memset(&option, 0, sizeof(option)); option.use_symbol_type = (1 << STT_OBJECT); return bcc_elf_foreach_sym(path.c_str(), sym_resolve_callback, &option, sym); #else std::string call_str = std::string("objdump -tT ") + path + " | grep -w " + sym->name; const char *call = call_str.c_str(); auto result = exec_system(call); sym->address = read_address_from_output(result); /* Trying to grab the size from objdump output is not that easy. foreaech_sym has been around for a while, users should switch to that. */ sym->size = 8; return 0; #endif } std::string BPFtrace::resolve_mac_address(const uint8_t *mac_addr) const { const size_t SIZE = 18; char addr[SIZE]; snprintf(addr, SIZE, "%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); return std::string(addr); } #ifdef HAVE_BCC_ELF_FOREACH_SYM static int add_symbol(const char *symname, uint64_t /*start*/, uint64_t /*size*/, void *payload) { auto syms = static_cast *>(payload); syms->insert(std::string(symname)); return 0; } #endif std::string BPFtrace::extract_func_symbols_from_path(const std::string &path) const { std::vector real_paths; if (path.find('*') != std::string::npos) real_paths = resolve_binary_path(path); else real_paths.push_back(path); #ifdef HAVE_BCC_ELF_FOREACH_SYM 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); #endif std::string result; for (auto &real_path : real_paths) { std::set syms; #ifdef HAVE_BCC_ELF_FOREACH_SYM // 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; } #else std::string call_str = std::string("objdump -tT ") + real_path + +" | " + "grep \"F .text\" | grep -oE '[^[:space:]]+$'"; const char *call = call_str.c_str(); std::istringstream iss(exec_system(call)); std::copy(std::istream_iterator(iss), std::istream_iterator(), std::inserter(syms, syms.begin())); #endif for (auto &sym : syms) result += real_path + ":" + sym + "\n"; } return result; } uint64_t BPFtrace::read_address_from_output(std::string output) { std::string first_word = output.substr(0, output.find(" ")); return std::stoull(first_word, 0, 16); } static std::string resolve_inetv4(const uint8_t* inet) { char addr_cstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, inet, addr_cstr, INET_ADDRSTRLEN); return std::string(addr_cstr); } static std::string resolve_inetv6(const uint8_t* inet) { char addr_cstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, inet, addr_cstr, INET6_ADDRSTRLEN); return std::string(addr_cstr); } std::string BPFtrace::resolve_inet(int af, const uint8_t* inet) const { std::string addrstr; switch (af) { case AF_INET: addrstr = resolve_inetv4(inet); break; case AF_INET6: addrstr = resolve_inetv6(inet); break; } // TODO(mmarchini): handle inet_ntop errors return addrstr; } // /proc/sys/kernel/randomize_va_space >= 1 and // system-wide // (/proc//personality & ADDR_NO_RNDOMIZE) == 0 // this pid // if pid == -1, then only check system-wide setting bool BPFtrace::is_aslr_enabled(int pid) { std::string randomize_va_space_file = "/proc/sys/kernel/randomize_va_space"; std::string personality_file = "/proc/" + std::to_string(pid) + "/personality"; { std::ifstream file(randomize_va_space_file); if (file.fail()) { if (bt_verbose) LOG(ERROR) << 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; } if (pid == -1) return true; { std::ifstream file(personality_file); if (file.fail()) { if (bt_verbose) LOG(ERROR) << strerror(errno) << ": " << personality_file; return true; } std::string line; if (std::getline(file, line) && ((std::stoi(line) & ADDR_NO_RANDOMIZE) == 0)) return true; } return false; } std::string BPFtrace::resolve_usym(uintptr_t addr, int pid, bool show_offset, bool show_module) { struct bcc_symbol usym; std::ostringstream symbol; void *psyms = nullptr; struct bcc_symbol_option symopts; memset(&symopts, 0, sizeof(symopts)); symopts.use_debug_file = 1; symopts.check_debug_file_crc = 1; symopts.use_symbol_type = BCC_SYM_ALL_TYPES; if (resolve_user_symbols_) { if (cache_user_symbols_) { std::string pid_exe = get_pid_exe(pid); if (exe_sym_.find(pid_exe) == exe_sym_.end()) { // not cached, create new ProcSyms cache psyms = bcc_symcache_new(pid, &symopts); exe_sym_[pid_exe] = std::make_pair(pid, psyms); } else { psyms = exe_sym_[pid_exe].second; } } else { psyms = bcc_symcache_new(pid, &symopts); } } if (psyms && bcc_symcache_resolve(psyms, addr, &usym) == 0) { if (demangle_cpp_symbols_) symbol << usym.demangle_name; else symbol << usym.name; if (show_offset) symbol << "+" << usym.offset; if (show_module) symbol << " (" << usym.module << ")"; } else { symbol << (void*)addr; if (show_module) symbol << " ([unknown])"; } if (psyms && !cache_user_symbols_) bcc_free_symcache(psyms, pid); return symbol.str(); } std::string BPFtrace::resolve_probe(uint64_t probe_id) const { assert(probe_id < resources.probe_ids.size()); return resources.probe_ids[probe_id]; } void BPFtrace::sort_by_key(std::vector key_args, std::vector, std::vector>> &values_by_key) { int arg_offset = 0; for (auto arg : key_args) { arg_offset += arg.GetSize(); } // Sort the key arguments in reverse order so the results are sorted by // the first argument first, then the second, etc. for (size_t i=key_args.size(); i-- > 0; ) { auto arg = key_args.at(i); arg_offset -= arg.GetSize(); if (arg.IsIntTy()) { if (arg.GetSize() == 8) { std::stable_sort( values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { auto va = read_data(a.first.data() + arg_offset); auto vb = read_data(b.first.data() + arg_offset); return va < vb; }); } else if (arg.GetSize() == 4) { std::stable_sort( values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { auto va = read_data(a.first.data() + arg_offset); auto vb = read_data(b.first.data() + arg_offset); return va < vb; }); } else { LOG(FATAL) << "invalid integer argument size. 4 or 8 expected, but " << arg.GetSize() << " provided"; } } else if (arg.IsStringTy()) { std::stable_sort( values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return strncmp((const char *)(a.first.data() + arg_offset), (const char *)(b.first.data() + arg_offset), arg.GetSize()) < 0; }); } // Other types don't get sorted } } std::string BPFtrace::get_string_literal(const ast::Expression *expr) const { if (expr->is_literal) { if (auto *string = dynamic_cast(expr)) return string->str; else if (auto *str_call = dynamic_cast(expr)) { // Positional parameters in the form str($1) can be used as literals if (str_call->func == "str") { if (auto *pos_param = dynamic_cast( str_call->vargs->at(0))) return get_param(pos_param->n, true); } } } LOG(ERROR) << "Expected string literal, got " << expr->type; return ""; } std::optional BPFtrace::get_int_literal(const ast::Expression *expr) const { if (expr->is_literal) { if (auto *integer = dynamic_cast(expr)) return integer->n; else if (auto *pos_param = dynamic_cast( expr)) { if (pos_param->ptype == PositionalParameterType::positional) { auto param_str = get_param(pos_param->n, false); if (is_numeric(param_str)) return std::stol(param_str); else { LOG(ERROR, pos_param->loc) << "$" << pos_param->n << " used numerically but given \"" << param_str << "\""; return std::nullopt; } } else return (long)num_params(); } } return std::nullopt; } bool BPFtrace::is_traceable_func(const std::string &func_name) const { #ifdef FUZZ (void)func_name; return true; #else return traceable_funcs_.find(func_name) != traceable_funcs_.end(); #endif } Dwarf *BPFtrace::get_dwarf(const std::string &filename) { auto dwarf = dwarves_.find(filename); if (dwarf == dwarves_.end()) { dwarf = dwarves_.emplace(filename, Dwarf::GetFromBinary(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); } } // namespace bpftrace bpftrace-0.14.0/src/bpftrace.h000066400000000000000000000152651413460502400161200ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include "ast/ast.h" #include "attached_probe.h" #include "bpffeature.h" #include "btf.h" #include "child.h" #include "dwarf_parser.h" #include "map.h" #include "mapmanager.h" #include "output.h" #include "printf.h" #include "probe_matcher.h" #include "procmon.h" #include "required_resources.h" #include "struct.h" #include "types.h" #include "utils.h" namespace bpftrace { struct symbol { std::string name; uint64_t start; uint64_t size; uint64_t address; }; enum class DebugLevel; // globals extern DebugLevel bt_debug; extern bool bt_quiet; extern bool bt_verbose; enum class DebugLevel { kNone, kDebug, kFullDebug }; inline DebugLevel operator++(DebugLevel &level, int) { switch (level) { case DebugLevel::kNone: level = DebugLevel::kDebug; break; case DebugLevel::kDebug: level = DebugLevel::kFullDebug; break; case DebugLevel::kFullDebug: // NOTE (mmarchini): should be handled by the caller level = DebugLevel::kNone; break; default: break; } return level; } class WildcardException : public std::exception { public: WildcardException(const std::string &msg) : msg_(msg) {} const char *what() const noexcept override { return msg_.c_str(); } private: std::string msg_; }; using BpfBytecode = std::unordered_map>; class BPFtrace { public: BPFtrace(std::unique_ptr o = std::make_unique(std::cout)) : traceable_funcs_(get_traceable_funcs()), out_(std::move(o)), feature_(std::make_unique()), probe_matcher_(std::make_unique(this)), btf_(this), ncpus_(get_possible_cpus().size()) { } virtual ~BPFtrace(); virtual int add_probe(ast::Probe &p); Probe generateWatchpointSetupProbe(const std::string &func, const ast::AttachPoint &ap, const ast::Probe &probe); int num_probes() const; int prerun() const; int run(BpfBytecode bytecode); std::vector> attach_probe( Probe &probe, BpfBytecode &bytecode); int run_iter(); int print_maps(); int clear_map(IMap &map); int zero_map(IMap &map); int print_map(IMap &map, uint32_t top, uint32_t div); std::string get_stack(uint64_t stackidpid, bool ustack, StackType stack_type, int indent=0); std::string resolve_buf(char *buf, size_t size); std::string resolve_ksym(uintptr_t addr, bool show_offset=false); std::string resolve_usym(uintptr_t addr, int pid, bool show_offset=false, bool show_module=false); std::string resolve_inet(int af, const uint8_t* inet) const; std::string resolve_uid(uintptr_t addr) const; std::string resolve_timestamp(uint32_t strftime_id, uint64_t nsecs); uint64_t resolve_kname(const std::string &name) const; virtual int resolve_uname(const std::string &name, struct symbol *sym, const std::string &path) const; std::string resolve_mac_address(const uint8_t *mac_addr) const; virtual std::string extract_func_symbols_from_path(const std::string &path) const; std::string resolve_probe(uint64_t probe_id) const; uint64_t resolve_cgroupid(const std::string &path) const; std::vector> get_arg_values(const std::vector &args, uint8_t* arg_data); void add_param(const std::string ¶m); std::string get_param(size_t index, bool is_str) const; size_t num_params() const; void request_finalize(); bool is_aslr_enabled(int pid); std::string get_string_literal(const ast::Expression *expr) const; std::optional get_int_literal(const ast::Expression *expr) const; std::optional get_watchpoint_binary_path() const; virtual bool is_traceable_func(const std::string &func_name) const; Dwarf *get_dwarf(const std::string &filename); Dwarf *get_dwarf(const ast::AttachPoint &attachpoint); std::vector> attached_probes_; std::string cmd_; bool finalize_ = false; // Global variable checking if an exit signal was received static volatile sig_atomic_t exitsig_recv; RequiredResources resources; MapManager maps; BpfBytecode bytecode_; StructManager structs; std::map macros_; std::map enums_; std::unordered_set traceable_funcs_; unsigned int join_argnum_ = 16; unsigned int join_argsize_ = 1024; std::unique_ptr out_; std::unique_ptr feature_; uint64_t strlen_ = 64; uint64_t mapmax_ = 4096; size_t cat_bytes_max_ = 10240; uint64_t max_probes_ = 512; uint64_t log_size_ = 1000000; uint64_t perf_rb_pages_ = 64; uint64_t max_type_res_iterations = 0; bool demangle_cpp_symbols_ = true; bool resolve_user_symbols_ = true; bool cache_user_symbols_ = true; bool safe_mode_ = true; bool has_usdt_ = false; bool usdt_file_activation_ = false; int helper_check_level_ = 0; uint64_t ast_max_nodes_ = 0; // Maximum AST nodes allowed for fuzzing std::optional boottime_; static void sort_by_key( std::vector key_args, std::vector, std::vector>> &values_by_key); std::unique_ptr probe_matcher_; BTF btf_; std::unordered_set btf_set_; std::map ap_args_; std::unique_ptr child_; std::unique_ptr procmon_; pid_t pid(void) const { return procmon_ ? procmon_->pid() : 0; } int ncpus_; int online_cpus_; private: int run_special_probe(std::string name, BpfBytecode &bytecode, void (*trigger)(void)); void* ksyms_{nullptr}; std::map> exe_sym_; // exe -> (pid, cache) std::vector params_; std::vector> open_perf_buffers_; std::vector> attach_usdt_probe( Probe &probe, std::tuple func, int pid, bool file_activation); int setup_perf_events(); void poll_perf_events(int epollfd, bool drain = false); int print_map_hist(IMap &map, uint32_t top, uint32_t div); int print_map_stats(IMap &map, uint32_t top, uint32_t div); static uint64_t read_address_from_output(std::string output); std::vector find_empty_key(IMap &map, size_t size) const; bool has_iter_ = false; std::unordered_map> dwarves_; }; } // namespace bpftrace bpftrace-0.14.0/src/btf.cpp000066400000000000000000000422601413460502400154330ustar00rootroot00000000000000#include "btf.h" #include "arch/arch.h" #include "bpftrace.h" #include "log.h" #include "probe_matcher.h" #include "types.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBBPF_BTF_DUMP #include #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" #include #pragma GCC diagnostic pop #include #include "bpftrace.h" namespace bpftrace { static unsigned char *get_data(const char *file, ssize_t *sizep) { struct stat st; if (stat(file, &st)) return nullptr; FILE *f; f = fopen(file, "rb"); if (!f) return nullptr; unsigned char *data; unsigned int size; size = st.st_size; data = (unsigned char *) malloc(size); if (!data) { fclose(f); return nullptr; } ssize_t ret = fread(data, 1, st.st_size, f); if (ret != st.st_size) { free(data); fclose(f); return nullptr; } fclose(f); *sizep = size; return data; } static struct btf* btf_raw(char *file) { unsigned char *data; ssize_t size; struct btf *btf; data = get_data(file, &size); if (!data) { LOG(ERROR) << "BTF: failed to read data from: " << file; return nullptr; } btf = btf__new(data, (__u32) size); free(data); return btf; } static int libbpf_print(enum libbpf_print_level level, const char *msg, va_list ap) { fprintf(stderr, "BTF: (%d) ", level); return vfprintf(stderr, msg, ap); } static struct btf *btf_open(const struct vmlinux_location *locs) { struct utsname buf; uname(&buf); for (int i = 0; locs[i].path; i++) { char path[PATH_MAX + 1]; snprintf(path, PATH_MAX, locs[i].path, buf.release); if (access(path, R_OK)) continue; struct btf *btf; if (locs[i].raw) btf = btf_raw(path); else btf = btf__parse_elf(path, nullptr); int err = libbpf_get_error(btf); if (err) { if (bt_debug != DebugLevel::kNone) { char err_buf[256]; libbpf_strerror(libbpf_get_error(btf), err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to read data (" << err_buf << ") from: " << path; } continue; } if (bt_debug != DebugLevel::kNone) { std::cerr << "BTF: using data from " << path << std::endl; } return btf; } return nullptr; } BTF::BTF(void) : btf(nullptr), state(NODATA) { struct vmlinux_location locs_env[] = { { nullptr, true }, { nullptr, false }, }; const struct vmlinux_location *locs = vmlinux_locs; // Try to get BTF file from BPFTRACE_BTF env char *path = std::getenv("BPFTRACE_BTF"); if (path) { locs_env[0].path = path; locs = locs_env; } btf = btf_open(locs); if (btf) { libbpf_set_print(libbpf_print); state = OK; } else if (bt_debug != DebugLevel::kNone) { LOG(ERROR) << "BTF: failed to find BTF data "; } } BTF::~BTF() { btf__free(btf); } static void dump_printf(void *ctx, const char *fmt, va_list args) { std::string *ret = static_cast(ctx); char *str; if (vasprintf(&str, fmt, args) < 0) return; *ret += str; free(str); } static const char *btf_str(const struct btf *btf, __u32 off) { if (!off) return "(anon)"; return btf__name_by_offset(btf, off) ? : "(invalid)"; } static std::string full_type_str(const struct btf *btf, const struct btf_type *type) { const char *str = btf_str(btf, type->name_off); if (BTF_INFO_KIND(type->info) == BTF_KIND_STRUCT) return std::string("struct ") + str; if (BTF_INFO_KIND(type->info) == BTF_KIND_UNION) return std::string("union ") + str; if (BTF_INFO_KIND(type->info) == BTF_KIND_ENUM) return std::string("enum ") + str; return str; } static std::string btf_type_str(const std::string& type) { return std::regex_replace(type, std::regex("^(struct )|(union )"), ""); } std::string BTF::c_def(const std::unordered_set &set) const { if (!has_data()) return std::string(""); std::string ret = std::string(""); struct btf_dump_opts opts = { .ctx = &ret, }; struct btf_dump *dump; char err_buf[256]; int err; dump = btf_dump__new(btf, nullptr, &opts, dump_printf); err = libbpf_get_error(dump); if (err) { libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return std::string(""); } std::unordered_set myset(set); __s32 id, max = (__s32) btf__get_nr_types(btf); for (id = 1; id <= max && myset.size(); id++) { const struct btf_type *t = btf__type_by_id(btf, id); // Allow users to reference enum values by name to pull in entire enum defs if (btf_is_enum(t)) { const struct btf_enum *p = btf_enum(t); uint16_t vlen = btf_vlen(t); for (int e = 0; e < vlen; ++e, ++p) { std::string str = btf_str(btf, p->name_off); auto it = myset.find(str); if (it != myset.end()) { btf_dump__dump_type(dump, id); myset.erase(it); break; } } } std::string str = full_type_str(btf, t); auto it = myset.find(str); if (it != myset.end()) { btf_dump__dump_type(dump, id); myset.erase(it); } } btf_dump__free(dump); return ret; } std::string BTF::type_of(const std::string& name, const std::string& field) { if (!has_data()) return std::string(""); __s32 type_id = btf__find_by_name(btf, btf_type_str(name).c_str()); if (type_id < 0) return std::string(""); const struct btf_type *type = btf__type_by_id(btf, type_id); return type_of(type, field); } std::string BTF::type_of(const btf_type *type, const std::string &field) { if (!has_data()) return std::string(""); if (!type || (BTF_INFO_KIND(type->info) != BTF_KIND_STRUCT && BTF_INFO_KIND(type->info) != BTF_KIND_UNION)) return std::string(""); // We need to walk through oaa the struct/union members // and try to find the requested field name. // // More info on struct/union members: // https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-union const struct btf_member *m = reinterpret_cast(type + 1); for (unsigned int i = 0; i < BTF_INFO_VLEN(type->info); i++) { std::string m_name = btf__name_by_offset(btf, m[i].name_off); // anonymous struct/union if (m_name == "") { const struct btf_type *type = btf__type_by_id(btf, m[i].type); std::string type_name = type_of(type, field); if (!type_name.empty()) return type_name; } if (m_name != field) continue; const struct btf_type *f = btf__type_by_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(btf, f->type); } return full_type_str(btf, f); } return std::string(""); } static bool btf_type_is_modifier(const struct btf_type *t) { // Some of them is not strictly a C modifier // but they are grouped into the same bucket // for BTF concern: // A type (t) that refers to another // type through t->type AND its size cannot // be determined without following the t->type. // ptr does not fall into this bucket // because its size is always sizeof(void *). switch (BTF_INFO_KIND(t->info)) { case BTF_KIND_TYPEDEF: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: return true; default: return false; } } const struct btf_type *BTF::btf_type_skip_modifiers(const struct btf_type *t) { while (btf_type_is_modifier(t)) { t = btf__type_by_id(btf, t->type); } return t; } SizedType BTF::get_stype(__u32 id) { const struct btf_type *t = btf__type_by_id(btf, id); if (!t) return CreateNone(); t = btf_type_skip_modifiers(t); auto stype = CreateNone(); if (btf_is_int(t) || btf_is_enum(t)) { stype = CreateInteger(btf_int_bits(t), btf_int_encoding(t) & BTF_INT_SIGNED); } else if (btf_is_composite(t)) { const char *cast = btf_str(btf, t->name_off); assert(cast); std::string comp = btf_is_struct(t) ? "struct" : "union"; std::string name = comp + " " + cast; // We're usually resolving types before running ClangParser, so the struct // definitions are not yet pulled into the struct map. We initialize them // now and fill them later. stype = CreateRecord(name, bpftrace_->structs.LookupOrAdd(name, t->size)); } else if (btf_is_ptr(t)) { // t->type is the pointee type stype = CreatePointer(get_stype(t->type)); } return stype; } int BTF::resolve_args(const std::string &func, std::map &args, bool ret) { if (!has_data()) throw std::runtime_error("BTF data not available"); __s32 id, max = (__s32)btf__get_nr_types(btf); std::string name = func; for (id = 1; id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); if (!btf_is_func(t)) continue; const char *str = btf_str(btf, t->name_off); if (name != str) continue; t = btf__type_by_id(btf, t->type); if (!btf_is_func_proto(t)) { throw std::runtime_error("not a function"); } if (bpftrace_ && !bpftrace_->is_traceable_func(name)) { if (bpftrace_->traceable_funcs_.empty()) throw std::runtime_error("could not read traceable functions from " + kprobe_path + " (is debugfs mounted?)"); else throw std::runtime_error("function not traceable (probably it is " "inlined or marked as \"notrace\")"); } const struct btf_param *p = btf_params(t); __u16 vlen = btf_vlen(t); if (vlen > arch::max_arg() + 1) { throw std::runtime_error("functions with more than 6 parameters are " "not supported."); } int j = 0; for (; j < vlen; j++, p++) { str = btf_str(btf, p->name_off); if (!str) { throw std::runtime_error("failed to resolve arguments"); } SizedType stype = get_stype(p->type); stype.funcarg_idx = j; stype.is_funcarg = true; args.insert({ str, stype }); } if (ret) { SizedType stype = get_stype(t->type); stype.funcarg_idx = j; stype.is_funcarg = true; args.insert({ "$retval", stype }); } return 0; } throw std::runtime_error("no BTF data for the function"); } std::unique_ptr BTF::get_all_funcs() const { __s32 id, max = (__s32)btf__get_nr_types(btf); std::string type = std::string(""); struct btf_dump_opts opts = { .ctx = &type, }; struct btf_dump *dump; std::string funcs; char err_buf[256]; int err; dump = btf_dump__new(btf, nullptr, &opts, dump_printf); err = libbpf_get_error(dump); if (err) { libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return nullptr; } for (id = 1; id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); if (!btf_is_func(t)) continue; const char *str = btf__name_by_offset(btf, t->name_off); std::string func_name = str; t = btf__type_by_id(btf, t->type); if (!btf_is_func_proto(t)) { /* bad.. */ if (!bt_verbose) LOG(ERROR) << func_name << " function does not have FUNC_PROTO record"; break; } if (bpftrace_ && !bpftrace_->is_traceable_func(func_name)) continue; if (btf_vlen(t) > arch::max_arg() + 1) continue; funcs += std::string(func_name) + "\n"; } if (id != (max + 1)) LOG(ERROR) << "BTF data inconsistency " << id << "," << max; btf_dump__free(dump); return std::make_unique(funcs); } std::map> BTF::get_params( const std::set &funcs) const { #ifdef HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL __s32 id, max = (__s32)btf__get_nr_types(btf); std::string type = std::string(""); struct btf_dump_opts opts = { .ctx = &type, }; struct btf_dump *dump; char err_buf[256]; int err; dump = btf_dump__new(btf, nullptr, &opts, dump_printf); err = libbpf_get_error(dump); if (err) { libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } std::map> params; for (id = 1; id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); if (!btf_is_func(t)) continue; const char *str = btf__name_by_offset(btf, t->name_off); std::string func_name = str; if (funcs.find(func_name) == funcs.end()) continue; t = btf__type_by_id(btf, t->type); _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") DECLARE_LIBBPF_OPTS(btf_dump_emit_type_decl_opts, decl_opts, .field_name = ""); _Pragma("GCC diagnostic pop") const struct btf_param *p; int j; for (j = 0, p = btf_params(t); j < btf_vlen(t); j++, p++) { // set by dump_printf callback type = std::string(""); const char *arg_name = btf__name_by_offset(btf, p->name_off); err = btf_dump__emit_type_decl(dump, p->type, &decl_opts); if (err) { LOG(ERROR) << "failed to dump argument: " << arg_name; break; } params[func_name].push_back(type + " " + arg_name); } if (!t->type) continue; // set by dump_printf callback type = std::string(""); err = btf_dump__emit_type_decl(dump, t->type, &decl_opts); if (err) { LOG(ERROR) << "failed to dump return type for: " << func_name; break; } params[func_name].push_back(type + " retval"); } if (id != (max + 1)) LOG(ERROR) << "BTF data inconsistency " << id << "," << max; btf_dump__free(dump); return params; #else LOG(ERROR) << "Could not get kfunc arguments " "(HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL is not set)" return {}; return {}; #endif } std::set BTF::get_all_structs() const { std::set struct_set; __s32 id, max = (__s32)btf__get_nr_types(btf); std::string types = std::string(""); struct btf_dump_opts opts = { .ctx = &types, }; struct btf_dump *dump; char err_buf[256]; int err; dump = btf_dump__new(btf, nullptr, &opts, dump_printf); err = libbpf_get_error(dump); if (err) { libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } for (id = 1; id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); if (!(btf_is_struct(t) || btf_is_union(t) || btf_is_enum(t))) continue; const std::string name = full_type_str(btf, t); if (name.find("(anon)") != std::string::npos) continue; if (bt_verbose) btf_dump__dump_type(dump, id); else struct_set.insert(name); } if (id != (max + 1)) LOG(ERROR) << " BTF data inconsistency " << id << "," << max; btf_dump__free(dump); if (bt_verbose) { // BTF dump contains definitions of all types in a single string, here we // split it std::istringstream type_stream(types); std::string line, type; bool in_def = false; while (std::getline(type_stream, line)) { if (in_def) { type += line + "\n"; if (line == "};") { // end of type definition struct_set.insert(type); type.clear(); in_def = false; } } else if (!line.empty() && line.back() == '{') { // start of type definition type += line + "\n"; in_def = true; } } } return struct_set; } } // namespace bpftrace #else // HAVE_LIBBPF_BTF_DUMP namespace bpftrace { BTF::BTF() { } BTF::~BTF() { } std::string BTF::c_def(const std::unordered_set& set __attribute__((__unused__))) const { return std::string(""); } std::string BTF::type_of(const std::string& name __attribute__((__unused__)), const std::string& field __attribute__((__unused__))) { return std::string(""); } int BTF::resolve_args(const std::string &func __attribute__((__unused__)), std::map& args __attribute__((__unused__)), bool ret __attribute__((__unused__))) { return -1; } std::set BTF::get_all_structs() const { return {}; } std::unique_ptr BTF::get_all_funcs() const { return nullptr; } std::map> BTF::get_params( const std::set& funcs __attribute__((__unused__))) const { return {}; } } // namespace bpftrace #endif // HAVE_LIBBPF_BTF_DUMP bpftrace-0.14.0/src/btf.h000066400000000000000000000023771413460502400151050ustar00rootroot00000000000000#pragma once #include "types.h" #include #include #include #include #include #include #include struct btf; struct btf_type; namespace bpftrace { class BPFtrace; class BTF { enum state { NODATA, OK, }; public: BTF(); BTF(BPFtrace* bpftrace) : BTF() { bpftrace_ = bpftrace; }; ~BTF(); bool has_data(void) const; std::string c_def(const std::unordered_set& set) const; std::string type_of(const std::string& name, const std::string& field); std::string type_of(const btf_type* type, const std::string& field); std::set get_all_structs() const; std::unique_ptr get_all_funcs() const; std::map> get_params( const std::set& funcs) const; int resolve_args(const std::string &func, std::map& args, bool ret); private: SizedType get_stype(__u32 id); const struct btf_type* btf_type_skip_modifiers(const struct btf_type* t); struct btf* btf; enum state state = NODATA; BPFtrace* bpftrace_ = nullptr; }; inline bool BTF::has_data(void) const { return state == OK; } } // namespace bpftrace bpftrace-0.14.0/src/build_info.cpp000066400000000000000000000035271413460502400167750ustar00rootroot00000000000000#include #include "build_info.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 #ifdef LLVM_ORC_V2 << " ORC: v2" << std::endl #endif << " foreach_sym: " #ifdef HAVE_BCC_ELF_FOREACH_SYM << "yes" << std::endl #else << "no" << std::endl #endif << " unsafe uprobe: " #ifdef HAVE_UNSAFE_UPROBE << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " bfd: " #ifdef HAVE_BFD_DISASM << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " bpf_attach_kfunc: " #ifdef HAVE_BCC_KFUNC << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " bcc_usdt_addsem: " #ifdef HAVE_BCC_USDT_ADDSEM << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " bcc bpf_attach_uprobe refcount: " #ifdef LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " bcc library path resolution: " #ifdef HAVE_BCC_WHICH_SO << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " libbpf: " #ifdef HAVE_LIBBPF << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " libbpf btf dump: " #ifdef HAVE_LIBBPF_BTF_DUMP << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " libbpf btf dump type decl: " #ifdef HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " libdw (DWARF support): " #ifdef HAVE_LIBDW << "yes" << std::endl; #else << "no" << std::endl; #endif return buf.str(); } } // namespace bpftrace bpftrace-0.14.0/src/build_info.h000066400000000000000000000002141413460502400164300ustar00rootroot00000000000000#pragma once #include namespace bpftrace { class BuildInfo { public: static std::string report(); }; } // namespace bpftrace bpftrace-0.14.0/src/child.cpp000066400000000000000000000152551413460502400157470ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "child.h" #include "log.h" #include "utils.h" extern char** environ; namespace bpftrace { constexpr unsigned int maxargs = 256; constexpr uint64_t CHILD_GO = 'g'; constexpr uint64_t CHILD_PTRACE = 'p'; constexpr unsigned int STACK_SIZE = (64 * 1024UL); std::system_error SYS_ERROR(std::string msg) { return std::system_error(errno, std::generic_category(), msg); } static void report_status(int wstatus) { std::stringstream msg; if (WIFSTOPPED(wstatus)) msg << "Child stopped unexpectedly, signal: " << WSTOPSIG(wstatus); else if (WIFEXITED(wstatus)) msg << "Child exited unexpectedly"; else if (WIFSIGNALED(wstatus)) { if (WCOREDUMP(wstatus)) msg << "Child core dumped"; else msg << "Child aborted by signal: " << WTERMSIG(wstatus); } throw std::runtime_error(msg.str()); } static int childfn(void* arg) { struct child_args* args = static_cast(arg); // Receive SIGTERM if parent dies if (prctl(PR_SET_PDEATHSIG, SIGTERM)) { perror("child: prctl(PR_SET_PDEATHSIG)"); return 10; } // Convert vector of strings into raw array of C-strings for execve(2) char* argv[maxargs]; int idx = 0; for (const auto& a : args->cmd) { argv[idx++] = const_cast(a.c_str()); } argv[idx] = nullptr; // must be null terminated uint64_t bf; int ret = read(args->event_fd, &bf, sizeof(bf)); if (ret < 0) { perror("child: failed to read 'go' event fd"); return 11; } close(args->event_fd); if (bf == CHILD_PTRACE) { if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) perror("child: ptrace(traceme) failed"); if (kill(getpid(), SIGSTOP)) perror("child: failed to stop"); } execve(argv[0], argv, environ); auto err = "child: failed to execve: " + std::string(argv[0]); perror(err.c_str()); return 12; } static void validate_cmd(std::vector& cmd) { auto paths = resolve_binary_path(cmd[0]); switch (paths.size()) { case 0: throw std::runtime_error("path '" + cmd[0] + "' does not exist or is not executable"); case 1: cmd[0] = paths.front().c_str(); break; default: throw std::runtime_error("path '" + cmd[0] + "' must refer to a unique binary but matched " + std::to_string(paths.size()) + " binaries"); return; } if (cmd.size() >= (maxargs - 1)) { throw std::runtime_error("Too many arguments for command (" + std::to_string(cmd.size()) + " > " + std::to_string(maxargs - 1) + ")"); } } ChildProc::ChildProc(std::string cmd) { auto child_args = std::make_unique(); auto child_stack = std::make_unique(STACK_SIZE); child_args->cmd = split_string(cmd, ' '); validate_cmd(child_args->cmd); int event_fd = eventfd(0, EFD_CLOEXEC); if (event_fd < 0) { SYS_ERROR("Failed to create event fd"); } child_args->event_fd = event_fd; child_event_fd_ = event_fd; pid_t cpid = clone( childfn, child_stack.get() + STACK_SIZE, SIGCHLD, child_args.get()); if (cpid <= 0) { close(event_fd); throw SYS_ERROR("Failed to clone child"); } child_pid_ = cpid; state_ = State::FORKED; } ChildProc::~ChildProc() { if (child_event_fd_ >= 0) { close(child_event_fd_); } if (is_alive()) terminate(true); } bool ChildProc::is_alive() { if (!died()) check_child(); return !died(); } void ChildProc::terminate(bool force) { // Make sure child didn't terminate in mean time check_child(); if (died()) return; if (child_pid_ <= 1) throw std::runtime_error("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(void) { assert(state_ == State::PTRACE_PAUSE); ptrace(PTRACE_DETACH, child_pid_, nullptr, 0); } void ChildProc::run(bool pause) { if (!is_alive()) { throw std::runtime_error("Child died unexpectedly"); } assert(state_ == State::FORKED); auto* data = pause ? &CHILD_PTRACE : &CHILD_GO; if (write(child_event_fd_, data, sizeof(*data)) < 0) { close(child_event_fd_); terminate(true); throw SYS_ERROR("Failed to write 'go' event fd"); } close(child_event_fd_); if (!pause) { state_ = State::RUNNING; return; } state_ = State::PTRACE_PAUSE; // After receiving the ptrace message the child will setup // ptrace and SIGSTOP itself. // we can then setup ptrace to stop the child right after execve // and let the child run until that point int wstatus; if (waitpid(child_pid_, &wstatus, 0) < 0) { if (errno == ECHILD) throw std::runtime_error("Child died unexpectedly"); } if (!WIFSTOPPED(wstatus) || WSTOPSIG(wstatus) != SIGSTOP) report_status(wstatus); try { if (ptrace(PTRACE_SETOPTIONS, child_pid_, nullptr, PTRACE_O_TRACEEXEC) < 0) throw SYS_ERROR("Failed to PTRACE_SETOPTIONS child"); if (ptrace(PTRACE_CONT, child_pid_, nullptr, 0) < 0) throw SYS_ERROR("Failed to PTRACE_CONT child"); if (waitpid(child_pid_, &wstatus, 0) < 0) throw SYS_ERROR("Error while waiting for child"); if (WIFSTOPPED(wstatus) && wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) return; report_status(wstatus); } catch (const std::runtime_error& e) { ptrace(PTRACE_DETACH, child_pid_, nullptr, 0); terminate(true); throw SYS_ERROR("Failed to write 'go' event fd"); } } // private void ChildProc::check_wstatus(int wstatus) { if (WIFEXITED(wstatus)) exit_code_ = WEXITSTATUS(wstatus); else if (WIFSIGNALED(wstatus)) term_signal_ = WTERMSIG(wstatus); // Ignore STOP and CONT else return; state_ = State::DIED; } void ChildProc::check_child(bool block) { int status = 0; int flags = WNOHANG; if (block) flags &= ~WNOHANG; pid_t ret; while ((ret = waitpid(child_pid_, &status, flags)) < 0 && errno == EINTR) ; if (ret < 0) { if (errno == EINVAL) throw std::runtime_error("BUG: waitpid() EINVAL"); else { LOG(ERROR) << "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.14.0/src/child.h000066400000000000000000000055721413460502400154150ustar00rootroot00000000000000#pragma once #include #include #include #include namespace bpftrace { struct child_args { std::vector cmd; int event_fd; }; class ChildProcBase { public: /** Parse command and fork a child process. \param cmd Command to run */ ChildProcBase() = default; virtual ~ChildProcBase() = default; /** let child run (execve). \param pause If set the child will be paused(stopped) just after `execve`. To resume the child `resume` will have to be called. */ virtual void run(bool pause = false) = 0; /** Ask child to terminate \param force Forcefully kill the child (SIGKILL) */ virtual void terminate(bool force = false) = 0; /** Whether the child process is still alive or not */ virtual bool is_alive() = 0; /** return the child pid */ pid_t pid() { return child_pid_; }; /** Get child exit code, if any. This should only be called when the child has finished (i.e. when is_alive() returns false) \return The exit code of the child or -1 if the child hasn't been terminated (by a signal) */ int exit_code() { return exit_code_; }; /** Get termination signal, if any. This should only be called when the child has finished (i.e. when is_alive() returns false) \return A signal ID or -1 if the child hasn't been terminated (by a signal) */ int term_signal() { return term_signal_; }; /** Resume a paused child. Only valid when run() has been called with pause=true */ virtual void resume(void) = 0; protected: pid_t child_pid_ = -1; int exit_code_ = -1; int term_signal_ = -1; }; class ChildProc : public ChildProcBase { public: /** Parse command and fork a child process. The child is run with the same permissions and environment variables as bpftrace. \param the command to run, with up to 255 optional arguments. If the executables path isn't fully specified it the current PATH will be searched. If more than one binary with the same name is found in the PATH an exception is raised. */ ChildProc(std::string cmd); ~ChildProc(); // Disallow copying as the internal state will get out of sync which will // cause issues. ChildProc(const ChildProc&) = delete; ChildProc& operator=(const ChildProc&) = delete; ChildProc(ChildProc&&) = delete; ChildProc& operator=(ChildProc&&) = delete; void run(bool pause = false) override; void terminate(bool force = false) override; bool is_alive() override; void resume(void) override; private: enum class State { INIT, FORKED, RUNNING, DIED, PTRACE_PAUSE, }; State state_ = State::INIT; void check_child(bool block = false); void check_wstatus(int wstatus); bool died() { return state_ == State::DIED; }; int child_event_fd_ = -1; }; } // namespace bpftrace bpftrace-0.14.0/src/clang_parser.cpp000066400000000000000000000707251413460502400173270ustar00rootroot00000000000000#include #include #include #include #include #include "llvm/Config/llvm-config.h" #include "ast/ast.h" #include "ast/passes/field_analyser.h" #include "btf.h" #include "clang_parser.h" #include "headers.h" #include "log.h" #include "types.h" #include "utils.h" namespace bpftrace { namespace { const std::vector &getDefaultHeaders() { static std::vector unsaved_files = { { .Filename = "/bpftrace/include/__stddef_max_align_t.h", .Contents = __stddef_max_align_t_h, .Length = __stddef_max_align_t_h_len, }, { .Filename = "/bpftrace/include/float.h", .Contents = float_h, .Length = float_h_len, }, { .Filename = "/bpftrace/include/limits.h", .Contents = limits_h, .Length = limits_h_len, }, { .Filename = "/bpftrace/include/stdarg.h", .Contents = stdarg_h, .Length = stdarg_h_len, }, { .Filename = "/bpftrace/include/stddef.h", .Contents = stddef_h, .Length = stddef_h_len, }, { .Filename = "/bpftrace/include/stdint.h", .Contents = stdint_h, .Length = stdint_h_len, }, { .Filename = "/bpftrace/include/" CLANG_WORKAROUNDS_H, .Contents = clang_workarounds_h, .Length = clang_workarounds_h_len, }, }; return unsaved_files; } std::vector getTranslationUnitFiles( const CXUnsavedFile &main_file) { std::vector files; files.reserve(1 + files.size()); files.emplace_back(main_file); const auto &dfl = getDefaultHeaders(); files.insert(files.end(), dfl.cbegin(), dfl.cend()); return files; } } // namespace static std::string get_clang_string(CXString string) { std::string str = clang_getCString(string); clang_disposeString(string); return str; } /* * is_anonymous * * Determine whether the provided cursor points to an anonymous struct. * * This union is anonymous: * struct { int i; }; * This is not, although it is marked as such in LLVM 8: * struct { int i; } obj; * This is not, and does not actually declare an instance of a struct: * struct X { int i; }; * * The libclang API was changed in LLVM 8 and restored under a different * function in LLVM 9. For LLVM 8 there is no way to properly tell if * a record declaration is anonymous, so we do some hacks here. * * LLVM version differences: * https://reviews.llvm.org/D54996 * https://reviews.llvm.org/D61232 */ static bool is_anonymous(CXCursor c) { #if LLVM_VERSION_MAJOR <= 7 return clang_Cursor_isAnonymous(c); #elif LLVM_VERSION_MAJOR >= 9 return clang_Cursor_isAnonymousRecordDecl(c); #else // LLVM 8 if (!clang_Cursor_isAnonymous(c)) return false; // In LLVM 8, some structs which the above function says are anonymous // are actually not. We iterate through the siblings of our struct // definition to see if there is a field giving it a name. // // struct Parent struct Parent // { { // struct struct // { { // ... ... // } name; }; // int sibling; int sibling; // }; }; // // Children of parent: Children of parent: // Struct: (cursor c) Struct: (cursor c) // Field: (Record)name Field: (int)sibling // Field: (int)sibling // // Record field found after No record field found after // cursor - not anonymous cursor - anonymous auto parent = clang_getCursorSemanticParent(c); if (clang_Cursor_isNull(parent)) return false; struct AnonFinderState { CXCursor struct_to_check; bool is_anon; bool prev_was_definition; } state; state.struct_to_check = c; state.is_anon = true; state.prev_was_definition = false; clang_visitChildren( parent, [](CXCursor c2, CXCursor, CXClientData client_data) { auto state = static_cast(client_data); if (state->prev_was_definition) { // This is the next child after the definition of the struct we're // interested in. If it is a field containing a record, we assume // that it must be the field for our struct, so our struct is not // anonymous. state->prev_was_definition = false; auto kind = clang_getCursorKind(c2); auto type = clang_getCanonicalType(clang_getCursorType(c2)); if (kind == CXCursor_FieldDecl && type.kind == CXType_Record) { state->is_anon = false; return CXChildVisit_Break; } } // We've found the definition of the struct we're interested in if (memcmp(c2.data, state->struct_to_check.data, 3*sizeof(uintptr_t)) == 0) state->prev_was_definition = true; return CXChildVisit_Continue; }, &state); return state.is_anon; #endif } /* * 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) && is_anonymous(parent)) { parent = clang_getCursorSemanticParent(parent); } return parent; } // @returns true on success, false otherwise static bool getBitfield(CXCursor c, Bitfield &bitfield) { if (!clang_Cursor_isBitField(c)) { return false; } // Algorithm description: // To handle bitfields, we need to give codegen 3 additional pieces // of information: `read_bytes`, `access_rshift`, and `mask`. // // `read_bytes` tells codegen how many bytes to read starting at `Field::offset`. // This information is necessary because we can't always issue, for example, a // 1 byte read, as the bitfield could be the last 4 bits of the struct. Reading // past the end of the struct could cause a page fault. Therefore, we compute the // minimum number of bytes necessary to fully read the bitfield. This will always // keep the read within the bounds of the struct. // // `access_rshift` tells codegen how much to shift the masked value so that the // LSB of the bitfield is the LSB of the interpreted integer. // // `mask` tells codegen how to mask out the surrounding bitfields. size_t bitfield_offset = clang_Cursor_getOffsetOfField(c) % 8; size_t bitfield_bitwidth = clang_getFieldDeclBitWidth(c); size_t bitfield_bitdidth_max = sizeof(uint64_t) * 8; if (bitfield_bitwidth > bitfield_bitdidth_max) { LOG(WARNING) << "bitfiled bitwidth " << bitfield_bitwidth << "is not supporeted." << " Use bitwidth " << bitfield_bitdidth_max; bitfield_bitwidth = bitfield_bitdidth_max; } if (bitfield_bitwidth == bitfield_bitdidth_max) bitfield.mask = std::numeric_limits::max(); else bitfield.mask = (1ULL << bitfield_bitwidth) - 1; // Round up to nearest byte bitfield.read_bytes = (bitfield_offset + bitfield_bitwidth + 7) / 8; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ bitfield.access_rshift = bitfield_offset; #else bitfield.access_rshift = (bitfield.read_bytes * 8 - bitfield_offset - bitfield_bitwidth); #endif return true; } // 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= DebugLevel::kDebug) LOG(ERROR) << "Input (" << input.size() << "): " << input; return false; } } return true; } CXCursor ClangParser::ClangParserHandler::get_translation_unit_cursor() { return clang_getTranslationUnitCursor(translation_unit); } bool ClangParser::ClangParserHandler::parse_file( const std::string &filename, const std::string &input, const std::vector &args, std::vector &unsaved_files, bool bail_on_errors) { StderrSilencer silencer; if (!bail_on_errors) silencer.silence(); CXErrorCode error = parse_translation_unit( filename.c_str(), args.data(), args.size(), unsaved_files.data(), unsaved_files.size(), CXTranslationUnit_DetailedPreprocessingRecord); error_msgs.clear(); if (error) { if (bt_debug == DebugLevel::kFullDebug) LOG(ERROR) << "Clang error while parsing C definitions: " << error; return false; } return check_diagnostics(input, bail_on_errors); } const std::vector &ClangParser::ClangParserHandler::get_error_messages() { return error_msgs; } bool ClangParser::ClangParserHandler::has_redefinition_error() { for (auto &msg : error_msgs) { if (msg.find("redefinition") != std::string::npos) return true; } return false; } bool ClangParser::ClangParserHandler::has_unknown_type_error() { for (auto &msg : error_msgs) { if (ClangParser::get_unknown_type(msg).has_value()) return true; } return false; } namespace { // Get annotation associated with field declaration `c` std::optional get_field_decl_annotation(CXCursor c) { assert(clang_getCursorKind(c) == CXCursor_FieldDecl); std::optional annotation; clang_visitChildren(c, [](CXCursor c, CXCursor __attribute__((unused)) parent, CXClientData data) { // The header generation code can annotate some struct // fields with additional information for us to parse // here. The annotation looks like: // // struct Foo { // __attribute__((annotate("tp_data_loc"))) int // name; // }; // // Currently only the TracepointFormatParser does this. if (clang_getCursorKind(c) == CXCursor_AnnotateAttr) { auto &a = *static_cast *>( data); a = get_clang_string(clang_getCursorSpelling(c)); } return CXChildVisit_Recurse; }, &annotation); return annotation; } } // namespace bool ClangParser::visit_children(CXCursor &cursor, BPFtrace &bpftrace) { int err = clang_visitChildren( cursor, [](CXCursor c, CXCursor parent, CXClientData client_data) { if (clang_getCursorKind(c) == CXCursor_MacroDefinition) { std::string macro_name; std::string macro_value; if (translateMacro(c, macro_name, macro_value)) { auto ¯os = static_cast(client_data)->macros_; macros[macro_name] = macro_value; } return CXChildVisit_Recurse; } if (clang_getCursorKind(parent) == CXCursor_EnumDecl) { auto &enums = static_cast(client_data)->enums_; enums[get_clang_string(clang_getCursorSpelling(c))] = clang_getEnumConstantDeclValue(c); return CXChildVisit_Recurse; } if (clang_getCursorKind(parent) != CXCursor_StructDecl && clang_getCursorKind(parent) != CXCursor_UnionDecl) return CXChildVisit_Recurse; if (clang_getCursorKind(c) == CXCursor_FieldDecl) { auto &structs = static_cast(client_data)->structs; auto named_parent = get_named_parent(c); auto ptype = clang_getCanonicalType(clang_getCursorType(named_parent)); auto ptypestr = get_unqualified_type_name(ptype); auto ptypesize = clang_Type_getSizeOf(ptype); auto ident = get_clang_string(clang_getCursorSpelling(c)); auto offset = clang_Type_getOffsetOf(ptype, ident.c_str()) / 8; auto type = clang_getCanonicalType(clang_getCursorType(c)); auto sized_type = get_sized_type(type, structs); Bitfield bitfield; bool is_bitfield = getBitfield(c, bitfield); 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); // No need to worry about redefined types b/c we should have already // checked clang diagnostics. The diagnostics will tell us if we have // duplicated types. structs.Lookup(ptypestr).lock()->AddField( ident, sized_type, offset, is_bitfield, bitfield, is_data_loc); } return CXChildVisit_Recurse; }, &bpftrace); // clang_visitChildren returns a non-zero value if the traversal // was terminated by the visitor returning CXChildVisit_Break. return err == 0; } std::unordered_set ClangParser::get_incomplete_types() { if (input.empty()) return {}; // Parse without failing on compilation errors (ie incomplete structs) because // our goal is to enumerate all such errors. ClangParserHandler handler; if (!handler.parse_file("definitions.h", input, args, input_files, false)) return {}; struct TypeData { std::unordered_set complete_types; std::unordered_set incomplete_types; } type_data; CXCursor cursor = handler.get_translation_unit_cursor(); clang_visitChildren( cursor, [](CXCursor c, CXCursor parent, CXClientData client_data) { auto &data = *static_cast(client_data); // We look for field declarations and store the parent // as a fully defined type because we know we're looking at a // type definition. // // Then look at the field declaration itself. If it's a record // type (ie struct or union), check if we think it's a fully // defined type. If not, add it to incomplete types set. if (clang_getCursorKind(parent) == CXCursor_EnumDecl || (clang_getCursorKind(c) == CXCursor_FieldDecl && (clang_getCursorKind(parent) == CXCursor_UnionDecl || clang_getCursorKind(parent) == CXCursor_StructDecl))) { auto parent_type = clang_getCanonicalType( clang_getCursorType(parent)); data.complete_types.emplace(get_unqualified_type_name(parent_type)); auto cursor_type = clang_getCanonicalType(clang_getCursorType(c)); // We need layouts of pointee types because users could dereference if (cursor_type.kind == CXType_Pointer) cursor_type = clang_getPointeeType(cursor_type); if (cursor_type.kind == CXType_Record) { auto type_name = get_unqualified_type_name(cursor_type); if (data.complete_types.find(type_name) == data.complete_types.end()) data.incomplete_types.emplace(std::move(type_name)); } } return CXChildVisit_Recurse; }, &type_data); return type_data.incomplete_types; } void ClangParser::resolve_incomplete_types_from_btf( BPFtrace &bpftrace, const ast::ProbeList *probes) { // Resolution of incomplete types must run at least once, maximum should be // the number of levels of nested field accesses for tracepoint args. // The maximum number of iterations can be also controlled by the // BPFTRACE_MAX_TYPE_RES_ITERATIONS env variable (0 is unlimited). uint64_t field_lvl = 1; for (auto &probe : *probes) if (probe->tp_args_structs_level > (int)field_lvl) field_lvl = probe->tp_args_structs_level; unsigned max_iterations = std::max(bpftrace.max_type_res_iterations, field_lvl); bool check_incomplete_types = true; for (unsigned i = 0; i < max_iterations && check_incomplete_types; i++) { // Collect incomplete types and retrieve their definitions from BTF. auto incomplete_types = get_incomplete_types(); size_t types_cnt = bpftrace.btf_set_.size(); bpftrace.btf_set_.insert(incomplete_types.cbegin(), incomplete_types.cend()); input_files.back() = get_btf_generated_header(bpftrace); // No need to continue if no more types were added check_incomplete_types = types_cnt != bpftrace.btf_set_.size(); } } /* * Parse the program using Clang. * * Type resolution rules: * * If BTF is available, necessary types are retrieved from there, otherwise we * rely on headers and types supplied by the user (we also include linux/types.h * in some cases, e.g., for tracepoints). * * The following types are taken from BTF (if available): * 1. Types explicitly used in the program (taken from bpftrace.btf_set_). * 2. Types used by some of the defined types (as struct members). This step * is done recursively, however, as it may take long time, there is a * maximal depth set. It is computed as the maximum level of nested field * accesses in the program and can be manually overridden using * the BPFTRACE_MAX_TYPE_RES_ITERATIONS env variable. * 3. Typedefs used by some of the defined types. These are also resolved * recursively, however, they must be resolved completely as any unknown * typedef will cause the parser to fail (even if the type is not used in * the program). * * If any of the above steps retrieves a definition that redefines some existing * (user-defined) type, no BTF types are used and all types must be provided. * In practice, this means that user may use kernel types without providing * their definitions but once he redefines any kernel type, he must provide all * necessary definitions. */ bool ClangParser::parse(ast::Program *program, BPFtrace &bpftrace, std::vector extra_flags) { #ifdef FUZZ StderrSilencer silencer; silencer.silence(); #endif input = "#include <__btf_generated_header.h>\n" + program->c_definitions; input_files = getTranslationUnitFiles(CXUnsavedFile{ .Filename = "definitions.h", .Contents = input.c_str(), .Length = input.size(), }); // clang-format off args = { "-isystem", "/usr/local/include", "-isystem", "/bpftrace/include", "-isystem", "/usr/include", }; // clang-format on 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.btf_.has_data() ? get_btf_generated_header(bpftrace) : get_empty_btf_generated_header()); bool btf_conflict = false; ClangParserHandler handler; if (bpftrace.btf_.has_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"); // Since we're omitting there's no reason to // add the wokarounds for it args.push_back("-D__CLANG_WORKAROUNDS_H"); if (handler.parse_file("definitions.h", input, args, input_files, false) && handler.has_redefinition_error()) btf_conflict = true; if (!btf_conflict) { resolve_incomplete_types_from_btf(bpftrace, program->probes); if (handler.parse_file( "definitions.h", input, 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", input, 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", input, 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."; } return false; } CXCursor cursor = handler.get_translation_unit_cursor(); return visit_children(cursor, bpftrace); } /* * Parse the given Clang diagnostics message and if it has the form: * unknown type name 'type_t' * return type_t. */ std::optional ClangParser::ClangParser::get_unknown_type( const std::string &diagnostic_msg) { const std::string unknown_type_msg = "unknown type name \'"; if (diagnostic_msg.find(unknown_type_msg) == 0) { return diagnostic_msg.substr(unknown_type_msg.length(), diagnostic_msg.length() - unknown_type_msg.length() - 1); } return {}; } std::unordered_set ClangParser::get_unknown_typedefs() { // Parse without failing on compilation errors (ie unknown types) because // our goal is to enumerate and analyse all such errors ClangParserHandler handler; if (!handler.parse_file("definitions.h", input, args, input_files, false)) return {}; std::unordered_set unknown_typedefs; // Search for error messages of the form: // unknown type name 'type_t' // that imply an unresolved typedef of type_t. This cannot be done in // clang_visitChildren since clang does not have the unknown type names. for (const auto &msg : handler.get_error_messages()) { auto unknown_type = get_unknown_type(msg); if (unknown_type) unknown_typedefs.emplace(unknown_type.value()); } return unknown_typedefs; } void ClangParser::resolve_unknown_typedefs_from_btf(BPFtrace &bpftrace) { bool check_unknown_types = true; while (check_unknown_types) { // Collect unknown typedefs and retrieve their definitions from BTF. // These must be resolved completely since any unknown typedef will cause // the parser to fail (even if that type is not used in the program). auto incomplete_types = get_unknown_typedefs(); size_t types_cnt = bpftrace.btf_set_.size(); bpftrace.btf_set_.insert(incomplete_types.cbegin(), incomplete_types.cend()); input_files.back() = get_btf_generated_header(bpftrace); // No need to continue if no more types were added check_unknown_types = types_cnt != bpftrace.btf_set_.size(); } } CXUnsavedFile ClangParser::get_btf_generated_header(BPFtrace &bpftrace) { btf_cdef = bpftrace.btf_.c_def(bpftrace.btf_set_); return CXUnsavedFile{ .Filename = "/bpftrace/include/__btf_generated_header.h", .Contents = btf_cdef.c_str(), .Length = btf_cdef.size(), }; } CXUnsavedFile ClangParser::get_empty_btf_generated_header() { btf_cdef = ""; return CXUnsavedFile{ .Filename = "/bpftrace/include/__btf_generated_header.h", .Contents = btf_cdef.c_str(), .Length = btf_cdef.size(), }; } } // namespace bpftrace bpftrace-0.14.0/src/clang_parser.h000066400000000000000000000064471413460502400167740ustar00rootroot00000000000000#pragma once #include #include "bpftrace.h" #include #define CLANG_WORKAROUNDS_H "clang_workarounds.h" namespace bpftrace { namespace ast { class Program; } class ClangParser { public: bool parse(ast::Program *program, BPFtrace &bpftrace, std::vector extra_flags = {}); private: bool visit_children(CXCursor &cursor, BPFtrace &bpftrace); /* * The user might have written some struct definitions that rely on types * supplied by BTF data. * * This method will pull out any forward-declared / incomplete struct * definitions and return the types (in string form) of the unresolved types. * * Note that this method does not report "errors". This is because the user * could have typo'd and actually referenced a non-existent type. Put * differently, this method is best effort. */ std::unordered_set get_incomplete_types(); /* * Iteratively check for incomplete types, pull their definitions from BTF, * and update the input files with the definitions. */ void resolve_incomplete_types_from_btf(BPFtrace &bpftrace, const ast::ProbeList *probes); /* * Collect names of types defined by typedefs that are in non-included * headers as they may pose problems for clang parser. */ std::unordered_set get_unknown_typedefs(); /* * Iteratively check for unknown typedefs, pull their definitions from BTF, * and update the input files with the definitions. */ void resolve_unknown_typedefs_from_btf(BPFtrace &bpftrace); static std::optional get_unknown_type( const std::string &diagnostic_msg); CXUnsavedFile get_btf_generated_header(BPFtrace &bpftrace); CXUnsavedFile get_empty_btf_generated_header(); std::string 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::string &input, 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(const std::string &input, bool bail_on_error); CXCursor get_translation_unit_cursor(); const std::vector &get_error_messages(); bool has_redefinition_error(); bool has_unknown_type_error(); private: CXIndex index; CXTranslationUnit translation_unit = nullptr; std::vector error_msgs; }; }; } // namespace bpftrace bpftrace-0.14.0/src/cxxdemangler/000077500000000000000000000000001413460502400166315ustar00rootroot00000000000000bpftrace-0.14.0/src/cxxdemangler/CMakeLists.txt000066400000000000000000000001561413460502400213730ustar00rootroot00000000000000add_library(cxxdemangler_stdlib cxxdemangler_stdlib.cpp) add_library(cxxdemangler_llvm cxxdemangler_llvm.cpp) bpftrace-0.14.0/src/cxxdemangler/cxxdemangler.h000066400000000000000000000002511413460502400214610ustar00rootroot00000000000000#pragma once namespace bpftrace { // Demangle a mangled C++ symbol name // // Note: callee `free()`ed char* cxxdemangle(const char* mangled); } // namespace bpftrace bpftrace-0.14.0/src/cxxdemangler/cxxdemangler_llvm.cpp000066400000000000000000000003361413460502400230520ustar00rootroot00000000000000#include "cxxdemangler.h" #include namespace bpftrace { char* cxxdemangle(const char* mangled) { return llvm::itaniumDemangle(mangled, nullptr, nullptr, nullptr); } } // namespace bpftrace bpftrace-0.14.0/src/cxxdemangler/cxxdemangler_stdlib.cpp000066400000000000000000000003141413460502400233550ustar00rootroot00000000000000#include "cxxdemangler.h" #include namespace bpftrace { char* cxxdemangle(const char* mangled) { return abi::__cxa_demangle(mangled, nullptr, nullptr, nullptr); } } // namespace bpftrace bpftrace-0.14.0/src/disasm.cpp000066400000000000000000000007561413460502400161440ustar00rootroot00000000000000#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.14.0/src/disasm.h000066400000000000000000000007551413460502400156100ustar00rootroot00000000000000#pragma once #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.14.0/src/driver.cpp000066400000000000000000000033231413460502400161500ustar00rootroot00000000000000#include #include "ast/attachpoint_parser.h" #include "driver.h" #include "log.h" #include "parser.tab.hh" extern void *yy_scan_string(const char *yy_str, yyscan_t yyscanner); extern int yylex_init(yyscan_t *scanner); extern int yylex_destroy (yyscan_t yyscanner); extern bpftrace::location loc; namespace bpftrace { Driver::Driver(BPFtrace &bpftrace, std::ostream &o) : bpftrace_(bpftrace), out_(o) { } Driver::~Driver() { delete root_; } void Driver::source(std::string filename, std::string script) { Log::get().set_source(filename, script); } // Kept for the test suite int Driver::parse_str(std::string script) { source("stdin", script); return parse(); } int Driver::parse() { // Ensure we free memory allocated the previous parse if we parse // more than once delete root_; root_ = nullptr; // Reset source location info on every pass loc.initialize(); yyscan_t scanner; yylex_init(&scanner); Parser parser(*this, scanner); if (debug_) { parser.set_debug_level(1); } yy_scan_string(Log::get().get_source().c_str(), scanner); parser.parse(); yylex_destroy(scanner); if (!failed_) { ast::AttachPointParser ap_parser(root_, bpftrace_, out_, listing_); if (ap_parser.parse()) failed_ = true; } if (failed_) { delete root_; root_ = nullptr; } // Keep track of errors thrown ourselves, since the result of // parser_->parse() doesn't take scanner errors into account, // only parser errors. return failed_; } void Driver::error(const location &l, const std::string &m) { LOG(ERROR, l, out_) << m; failed_ = true; } void Driver::error(const std::string &m) { LOG(ERROR, out_) << m; failed_ = true; } } // namespace bpftrace bpftrace-0.14.0/src/driver.h000066400000000000000000000013371413460502400156200ustar00rootroot00000000000000#pragma once #include #include #include "ast/ast.h" #include "bpftrace.h" typedef void* yyscan_t; namespace bpftrace { class Driver { public: explicit Driver(BPFtrace &bpftrace, std::ostream &o = std::cerr); ~Driver(); int parse(); int parse_str(std::string script); void source(std::string, std::string); void error(std::ostream &, const location &, const std::string &); void error(const location &l, const std::string &m); void error(const std::string &m); ast::Program *root_ = nullptr; void debug() { debug_ = true; }; BPFtrace &bpftrace_; bool listing_ = false; private: std::ostream &out_; bool failed_ = false; bool debug_ = false; }; } // namespace bpftrace bpftrace-0.14.0/src/dwarf_parser.cpp000066400000000000000000000125071413460502400173400ustar00rootroot00000000000000#include "dwarf_parser.h" #ifdef HAVE_LIBDW #include "log.h" #include #include namespace bpftrace { struct FuncInfo { std::string name; Dwarf_Die die; }; Dwarf::Dwarf(const std::string &file_path) : file_path_(file_path) { callbacks.find_debuginfo = dwfl_standard_find_debuginfo; callbacks.section_address = dwfl_offline_section_address; callbacks.debuginfo_path = NULL; dwfl = dwfl_begin(&callbacks); dwfl_report_offline(dwfl, file_path.c_str(), file_path.c_str(), -1); dwfl_report_end(dwfl, NULL, NULL); } std::unique_ptr Dwarf::GetFromBinary(const std::string &file_path) { std::unique_ptr dwarf(new Dwarf(file_path)); Dwarf_Addr bias; if (dwfl_nextcu(dwarf->dwfl, NULL, &bias) == NULL) 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_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 {}; Dwarf_Die param_die; Dwarf_Die *param_iter = ¶m_die; if (dwarf_child(&func_die.value(), ¶m_die) != 0) return {}; std::vector param_dies; do { if (dwarf_tag(¶m_die) == DW_TAG_formal_parameter) param_dies.push_back(param_die); } while (dwarf_siblingof(param_iter, ¶m_die) == 0); return param_dies; } 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 ""; } } SizedType Dwarf::get_stype(Dwarf_Die &type_die) 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_Attribute encoding_attr; Dwarf_Word encoding; dwarf_formudata( dwarf_attr_integrate(&type, DW_AT_encoding, &encoding_attr), &encoding); 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)); } // void * return CreatePointer(CreateNone()); } default: return CreateNone(); } } std::vector Dwarf::get_function_params( const std::string &function) const { std::vector result; for (auto ¶m_die : function_param_dies(function)) { const std::string name = dwarf_diename(¶m_die); Dwarf_Die type_die = type_of(param_die); result.push_back(get_type_name(type_die) + " " + name); } return result; } ProbeArgs Dwarf::resolve_args(const std::string &function) { std::map result; 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++; result.emplace(dwarf_diename(¶m_die), arg_type); } return result; } } // namespace bpftrace #endif // HAVE_LIBDWbpftrace-0.14.0/src/dwarf_parser.h000066400000000000000000000031531413460502400170020ustar00rootroot00000000000000#pragma once #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(const std::string &file_path); std::vector get_function_params( const std::string &function) const; ProbeArgs resolve_args(const std::string &function); private: explicit Dwarf(const std::string &file_path); std::vector function_param_dies(const std::string &function) const; std::optional get_func_die(const std::string &function) const; std::string get_type_name(Dwarf_Die &type_die) const; SizedType get_stype(Dwarf_Die &type_die) const; Dwfl *dwfl = nullptr; Dwfl_Callbacks callbacks; std::string file_path_; }; } // namespace bpftrace #else // HAVE_LIBDW #include "log.h" namespace bpftrace { class Dwarf { public: static std::unique_ptr GetFromBinary(const std::string &file_path_ __attribute__((unused))) { static bool warned = false; if (!warned) LOG(WARNING) << "Cannot parse DWARF: libdw not available"; warned = true; return nullptr; } std::vector get_function_params(const std::string &function __attribute__((unused))) const { return {}; } ProbeArgs resolve_args(const std::string &function __attribute__((unused))) { return {}; } }; } // namespace bpftrace #endif // HAVE_LIBDW bpftrace-0.14.0/src/fake_map.cpp000066400000000000000000000013761413460502400164260ustar00rootroot00000000000000#include "fake_map.h" namespace bpftrace { FakeMap::FakeMap(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step, int max_entries) : IMap(name, type, key, min, max, step, max_entries) { } FakeMap::FakeMap(const std::string &name, enum bpf_map_type type, int key_size, int value_size, int max_entries, int flags) : IMap(name, type, key_size, value_size, max_entries, flags) { } FakeMap::FakeMap(const SizedType &type) : IMap(type) { } FakeMap::FakeMap(enum bpf_map_type map_type) : IMap(map_type) { } } // namespace bpftrace bpftrace-0.14.0/src/fake_map.h000066400000000000000000000013261413460502400160660ustar00rootroot00000000000000#pragma once #include "map.h" namespace bpftrace { class FakeMap : public IMap { public: FakeMap(const std::string &name, const SizedType &type, const MapKey &key, int max_entries) : FakeMap(name, type, key, 0, 0, 0, max_entries){}; FakeMap(const SizedType &type); FakeMap(enum bpf_map_type map_type); FakeMap(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step, int max_entries); FakeMap(const std::string &name, enum bpf_map_type type, int key_size, int value_size, int max_entries, int flags); }; } // namespace bpftrace bpftrace-0.14.0/src/fuzz_main.cpp000066400000000000000000000104211413460502400166540ustar00rootroot00000000000000// main function for fuzzing // To build this, add -DBUILD_FUZZ=1 CMake option // The compiled binary name is "bpftrace_fuzz" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ast/bpforc/bpforc.h" #include "ast/clang_parser.h" #include "ast/passes/callback_visitor.h" #include "ast/passes/field_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpftrace.h" #include "codegen_llvm.h" #include "driver.h" #include "log.h" #include "output.h" #include "tracepoint_format_parser.h" #define DEFAULT_NODE_MAX 200 using namespace bpftrace; int fuzz_main(const char* data, size_t sz); #ifdef LIBFUZZER // main entry for libufuzzer // libfuzzer.a provides main function extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t sz) { fuzz_main((const char*)data, sz); return 0; // Non-zero return values are reserved for future use. } #else // main function for AFL, etc. int main(int argc, char* argv[]) { // Read inputs std::stringstream buf; std::string line; if (argc <= 1) { // Input from stdin (AFL's default) while (std::getline(std::cin, line)) buf << line << std::endl; } else { // Read from file std::string filename(argv[1]); std::ifstream file(filename); if (file.fail()) return 1; buf << file.rdbuf(); } const auto& str = buf.str(); return fuzz_main(str.c_str(), str.size()); } #endif // LIBFUZZER int fuzz_main(const char* data, size_t sz) { if (data == nullptr || sz == 0) return 0; if (getuid() != 0) return 1; DISABLE_LOG(DEBUG); DISABLE_LOG(INFO); DISABLE_LOG(WARNING); // We can't disable error logs because some functions use a length of error // log to see if an error occurs. Instead, suppress error log output at each // place. // DISABLE_LOG(ERROR); std::ofstream devnull; devnull.open("/dev/null", std::ofstream::out | std::ofstream::app); // reset global states TracepointFormatParser::clear_struct_list(); std::unique_ptr output; std::ostream* os = &std::cout; output = std::make_unique(*os); BPFtrace bpftrace(std::move(output)); bpftrace.safe_mode_ = 0; Driver driver(bpftrace); std::string script(data, sz); driver.source("fuzz", script); // Create AST auto err = driver.parse(); if (err) return err; // Limit node size uint64_t node_max = DEFAULT_NODE_MAX; if (!get_uint64_env_var("BPFTRACE_NODE_MAX", node_max)) return 1; uint64_t node_count = 0; ast::CallbackVisitor counter( [&](ast::Node* node __attribute__((unused))) { node_count += 1; }); driver.root_->accept(counter); if (node_count > node_max) return 1; // Field Analyzer ast::FieldAnalyser fields(driver.root_, bpftrace, devnull); err = fields.analyse(); if (err) return err; // Tracepoint parser if (TracepointFormatParser::parse(driver.root_, bpftrace) == false) return 1; // ClangParser ClangParser clang; std::vector extra_flags; { struct utsname utsname; uname(&utsname); std::string ksrc, kobj; auto kdirs = get_kernel_dirs(utsname, !bpftrace.features_->has_btf()); ksrc = std::get<0>(kdirs); kobj = std::get<1>(kdirs); if (ksrc != "") extra_flags = get_kernel_cflags(utsname.machine, ksrc, kobj); } extra_flags.push_back("-include"); extra_flags.push_back(CLANG_WORKAROUNDS_H); if (!clang.parse(driver.root_, bpftrace, extra_flags)) return 1; err = driver.parse(); if (err) return err; // Semantic Analyzer ast::SemanticAnalyser semantics(driver.root_, bpftrace, devnull, false); err = semantics.analyse(); if (err) return err; #if defined(TEST_SEMANTIC) return 0; #endif // Create maps err = semantics.create_maps(true); if (err) return err; // Codegen ast::CodegenLLVM llvm(driver.root_, bpftrace); std::unique_ptr bpforc; try { llvm.generate_ir(); llvm.optimize(); bpforc = llvm.emit(); } catch (const std::system_error& ex) { return 1; } catch (const std::exception& ex) { // failed to compile return 1; } // for debug // LOG(INFO) << "ok"; return 0; } bpftrace-0.14.0/src/headers.h000066400000000000000000000012131413460502400157310ustar00rootroot00000000000000#pragma once namespace bpftrace { // These externs are provided by our build system. See resources/CMakeLists.txt extern const char __stddef_max_align_t_h[]; extern const unsigned __stddef_max_align_t_h_len; extern const char float_h[]; extern const unsigned float_h_len; extern const char limits_h[]; extern const unsigned limits_h_len; extern const char stdarg_h[]; extern const unsigned stdarg_h_len; extern const char stddef_h[]; extern const unsigned stddef_h_len; extern const char stdint_h[]; extern const unsigned stdint_h_len; extern const char clang_workarounds_h[]; extern const unsigned clang_workarounds_h_len; } // namespace bpftrace bpftrace-0.14.0/src/imap.cpp000066400000000000000000000027401413460502400156050ustar00rootroot00000000000000#include "imap.h" #include namespace bpftrace { IMap::IMap(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step, int max_entries __attribute__((unused))) : name_(name), type_(type), key_(key), lqmin(min), lqmax(max), lqstep(step) { if (type.IsCountTy() && !key.args_.size()) { map_type_ = BPF_MAP_TYPE_PERCPU_ARRAY; } else if ((type.IsHistTy() || type.IsLhistTy() || type.IsCountTy() || type.IsSumTy() || type.IsMinTy() || type.IsMaxTy() || type.IsAvgTy() || type.IsStatsTy()) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))) { map_type_ = BPF_MAP_TYPE_PERCPU_HASH; } else { map_type_ = BPF_MAP_TYPE_HASH; } } IMap::IMap(const std::string &name, enum bpf_map_type type, int key_size __attribute__((unused)), int value_size __attribute__((unused)), int max_entries __attribute__((unused)), int flags __attribute__((unused))) : name_(name), map_type_(type) { } IMap::IMap(const SizedType &type) : type_(type), map_type_(BPF_MAP_TYPE_STACK_TRACE) { // This constructor should only be called with stack types assert(type.IsStack()); } IMap::IMap(enum bpf_map_type map_type) : map_type_(map_type) { // This constructor should only be called for perf events assert(map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY); } } // namespace bpftrace bpftrace-0.14.0/src/imap.h000066400000000000000000000027601413460502400152540ustar00rootroot00000000000000#pragma once #include #include "mapkey.h" #include "types.h" #include namespace bpftrace { class IMap { public: IMap(const std::string &name, const SizedType &type, const MapKey &key, int max_entries) : IMap(name, type, key, 0, 0, 0, max_entries){}; IMap(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step, int max_entries); IMap(const std::string &name, enum bpf_map_type type, int key_size, int value_size, int max_entries, int flags); IMap(const SizedType &type); IMap(enum bpf_map_type map_type); virtual ~IMap() = default; IMap(const IMap &) = delete; IMap &operator=(const IMap &) = delete; // unique id of this map. Used by runtime to reference this map uint32_t id = static_cast(-1); int mapfd_ = -1; std::string name_; SizedType type_; MapKey key_; enum bpf_map_type map_type_ = BPF_MAP_TYPE_UNSPEC; bool printable_ = true; // used by lhist(). TODO: move to separate Map object. int lqmin = 0; int lqmax = 0; int lqstep = 0; bool is_per_cpu_type() { return map_type_ == BPF_MAP_TYPE_PERCPU_HASH || map_type_ == BPF_MAP_TYPE_PERCPU_ARRAY; } bool is_clearable() const { return map_type_ != BPF_MAP_TYPE_ARRAY && map_type_ != BPF_MAP_TYPE_PERCPU_ARRAY; } bool is_printable() const { return printable_; } }; } // namespace bpftrace bpftrace-0.14.0/src/lexer.h000066400000000000000000000004411413460502400154370ustar00rootroot00000000000000#pragma once #include "driver.h" #include "parser.tab.hh" #define YY_DECL \ bpftrace::Parser::symbol_type yylex(bpftrace::Driver &driver, \ yyscan_t yyscanner) YY_DECL; bpftrace-0.14.0/src/lexer.l000066400000000000000000000262721413460502400154550ustar00rootroot00000000000000%option yylineno nodefault noyywrap noinput %option never-interactive %option reentrant %option stack %{ #include #include "driver.h" #include "utils.h" #include "parser.tab.hh" #include "ast/int_parser.h" #include "lexer.h" bpftrace::location loc; static std::string struct_type; static std::string buffer; #define YY_USER_ACTION loc.columns(yyleng); #define yyterminate() return bpftrace::Parser::make_END(loc) using namespace bpftrace; %} /* https://en.cppreference.com/w/cpp/language/integer_literal#The_type_of_the_literal */ int_size (([uU])|([uU]?[lL]?[lL])) /* Number with underscores in it, e.g. 1_000_000 */ int [0-9]([0-9_]*[0-9])?{int_size}? hex 0[xX][0-9a-fA-F]+ /* scientific notation, e.g. 2e4 or 1e6 */ exponent {int}[eE]{int} ident [_a-zA-Z][_a-zA-Z0-9]* map @{ident}|@ var ${ident} hspace [ \t] vspace [\n\r] space {hspace}|{vspace} path :(\\.|[_\-\./a-zA-Z0-9#\*])+ builtin arg[0-9]|args|cgroup|comm|cpid|cpu|ctx|curtask|elapsed|func|gid|nsecs|pid|probe|rand|retval|sarg[0-9]|tid|uid|username call avg|buf|cat|cgroupid|clear|count|delete|exit|hist|join|kaddr|kptr|ksym|lhist|macaddr|max|min|ntop|override|print|printf|reg|signal|sizeof|stats|str|strftime|strncmp|sum|system|time|uaddr|uptr|usym|zero|path|unwatch /* Don't add to this! Use builtin OR call not both */ call_and_builtin kstack|ustack /* 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 %% {hspace}+ { loc.step(); } {vspace}+ { loc.lines(yyleng); loc.step(); } ^"#!".*$ // executable line "//".*$ // single-line comments "/*" yy_push_state(COMMENT, yyscanner); { "*/" yy_pop_state(yyscanner); [^*\n]+|"*" {} \n loc.lines(1); loc.step(); <> yy_pop_state(yyscanner); driver.error(loc, "end of file during comment"); } bpftrace|perf { return Parser::make_STACK_MODE(yytext, loc); } {builtin} { return Parser::make_BUILTIN(yytext, loc); } {call} { return Parser::make_CALL(yytext, loc); } {call_and_builtin} { return Parser::make_CALL_BUILTIN(yytext, loc); } {int}|{hex}|{exponent} { try { auto res = ast::int_parser::to_uint(yytext, 0); return Parser::make_INT(res, loc); } catch (const std::exception &e) { driver.error(loc, e.what()); } } {path} { return Parser::make_PATH(yytext, loc); } {map} { return Parser::make_MAP(yytext, loc); } {var} { return Parser::make_VAR(yytext, loc); } ":" { return Parser::make_COLON(loc); } ";" { return Parser::make_SEMI(loc); } "{" { return Parser::make_LBRACE(loc); } "}" { return Parser::make_RBRACE(loc); } "[" { return Parser::make_LBRACKET(loc); } "]" { return Parser::make_RBRACKET(loc); } "(" { return Parser::make_LPAREN(loc); } ")" { return Parser::make_RPAREN(loc); } \//{space}*[\/\{] { return Parser::make_ENDPRED(loc); } /* If "/" is followed by "/" or "{", choose ENDPRED, otherwise DIV */ "," { return Parser::make_COMMA(loc); } "=" { return Parser::make_ASSIGN(loc); } "<<=" { return Parser::make_LEFTASSIGN(loc); } ">>=" { return Parser::make_RIGHTASSIGN(loc); } "+=" { return Parser::make_PLUSASSIGN(loc); } "-=" { return Parser::make_MINUSASSIGN(loc); } "*=" { return Parser::make_MULASSIGN(loc); } "/=" { return Parser::make_DIVASSIGN(loc); } "%=" { return Parser::make_MODASSIGN(loc); } "&=" { return Parser::make_BANDASSIGN(loc); } "|=" { return Parser::make_BORASSIGN(loc); } "^=" { return Parser::make_BXORASSIGN(loc); } "==" { return Parser::make_EQ(loc); } "!=" { return Parser::make_NE(loc); } "<=" { return Parser::make_LE(loc); } ">=" { return Parser::make_GE(loc); } "<<" { return Parser::make_LEFT(loc); } ">>" { return Parser::make_RIGHT(loc); } "<" { return Parser::make_LT(loc); } ">" { return Parser::make_GT(loc); } "&&" { return Parser::make_LAND(loc); } "||" { return Parser::make_LOR(loc); } "+" { return Parser::make_PLUS(loc); } "-" { return Parser::make_MINUS(loc); } "++" { return Parser::make_INCREMENT(loc); } "--" { return Parser::make_DECREMENT(loc); } "*" { return Parser::make_MUL(loc); } "/" { return Parser::make_DIV(loc); } "%" { return Parser::make_MOD(loc); } "&" { return Parser::make_BAND(loc); } "|" { return Parser::make_BOR(loc); } "^" { return Parser::make_BXOR(loc); } "!" { return Parser::make_LNOT(loc); } "~" { return Parser::make_BNOT(loc); } "." { return Parser::make_DOT(loc); } "->" { return Parser::make_PTR(loc); } "$"[0-9]+ { return Parser::make_PARAM(yytext, loc); } "$"# { return Parser::make_PARAMCOUNT(loc); } "#"[^!].* { return Parser::make_CPREPROC(yytext, loc); } "if" { return Parser::make_IF(loc); } "else" { return Parser::make_ELSE(loc); } "?" { return Parser::make_QUES(loc); } "unroll" { return Parser::make_UNROLL(loc); } "while" { return Parser::make_WHILE(loc); } "for" { return Parser::make_FOR(loc); } "return" { return Parser::make_RETURN(loc); } "continue" { return Parser::make_CONTINUE(loc); } "break" { return Parser::make_BREAK(loc); } \" { yy_push_state(STR, yyscanner); buffer.clear(); } { \" { yy_pop_state(yyscanner); return Parser::make_STRING(buffer, loc); } [^\\\n\"]+ buffer += yytext; \\n buffer += '\n'; \\t buffer += '\t'; \\r buffer += '\r'; \\\" buffer += '\"'; \\\\ buffer += '\\'; \\{oct_esc} { long value = strtol(yytext+1, NULL, 8); if (value > UCHAR_MAX) driver.error(loc, std::string("octal escape sequence out of range '") + yytext + "'"); buffer += value; } \\{hex_esc} buffer += strtol(yytext+2, NULL, 16); \n driver.error(loc, "unterminated string"); yy_pop_state(yyscanner); loc.lines(1); loc.step(); <> driver.error(loc, "unterminated string"); yy_pop_state(yyscanner); \\. { driver.error(loc, std::string("invalid escape character '") + yytext + "'"); } . driver.error(loc, "invalid character"); yy_pop_state(yyscanner); } struct|union|enum yy_push_state(STRUCT, yyscanner); buffer.clear(); struct_type = yytext; { "*"|")" { if (YY_START == STRUCT) { // Finished parsing the typename of a cast // Put the cast type into a canonical form by trimming // and then inserting a single space. yy_pop_state(yyscanner); unput(yytext[0]); return Parser::make_IDENT(struct_type + " " + trim(buffer), loc); } buffer += yytext[0]; } "{" yy_push_state(BRACE, yyscanner); buffer += '{'; "}"|"};" { buffer += yytext; yy_pop_state(yyscanner); if (YY_START == STRUCT) { // Finished parsing a struct definition // Trimming isn't needed here since the typenames // will go through Clang before we get them back // anyway. yy_pop_state(yyscanner); return Parser::make_STRUCT_DEFN(struct_type + buffer, loc); } } . buffer += yytext[0]; \n buffer += '\n'; loc.lines(1); loc.step(); } {ident} { static int unput_count = 0; if (driver.bpftrace_.macros_.count(yytext) != 0) { const char *s = driver.bpftrace_.macros_[yytext].c_str(); int z; // NOTE(mmarchini) workaround for simple recursive // macros. More complex recursive macros (for // example, with operators) will go into an // infinite loop. Yes, we should fix that in the // future. if (strcmp(s, yytext) == 0) { unput_count = 0; return Parser::make_IDENT(yytext, loc); } else { int leng = yyleng; std::string original_s(s); std::string original_yytext(yytext); for (z=strlen(s) - 1; z >= 0; z--){ if (unput_count >= 1000 || leng <= 0) { driver.error(loc, std::string("Macro recursion limit reached: ") + original_yytext + ", " + original_s); yyterminate(); } unput(s[z]); unput_count++; leng--; } } } else { unput_count = 0; return Parser::make_IDENT(yytext, loc); } } . { driver.error(loc, std::string("invalid character '") + std::string(yytext) + std::string("'")); } %% bpftrace-0.14.0/src/libbpf/000077500000000000000000000000001413460502400154065ustar00rootroot00000000000000bpftrace-0.14.0/src/libbpf/bpf.h000066400000000000000000000147571413460502400163440ustar00rootroot00000000000000#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 // 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, }; 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, }; 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, }; #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), /* 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 // clang-format on bpftrace-0.14.0/src/lockdown.cpp000066400000000000000000000043731413460502400165030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "lockdown.h" namespace bpftrace { namespace 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 bool is_ubuntu(void) { // If ubuntu is somewhere in uname it is probably ubuntu struct utsname name = {}; uname(&name); std::string version(name.version); std::transform(version.begin(), version.end(), version.begin(), [](unsigned char c) { return std::tolower(c); }); return (version.find("ubuntu") != std::string::npos); } static LockdownState read_security_lockdown(void) { 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/iovisor/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(std::unique_ptr &feature) { // Ubuntu (19.10 at least) ships a lockdown version that fully blocks the bpf // syscall if (is_ubuntu() && !feature->has_map_array() && !feature->has_helper_probe_read()) { return LockdownState::Confidentiality; } return read_security_lockdown(); } } // namespace lockdown } // namespace bpftrace bpftrace-0.14.0/src/lockdown.h000066400000000000000000000006101413460502400161360ustar00rootroot00000000000000#pragma once #include #include "bpffeature.h" namespace bpftrace { namespace lockdown { enum class LockdownState { None, Integrity, Confidentiality, Unknown, // Could not determine whether lockdown is enabled or not }; LockdownState detect(std::unique_ptr &feature); void emit_warning(std::ostream &out); } // namespace lockdown } // namespace bpftrace bpftrace-0.14.0/src/log.cpp000066400000000000000000000120221413460502400154320ustar00rootroot00000000000000#include "log.h" namespace bpftrace { std::string logtype_str(LogType t) { switch (t) { // clang-format off case LogType::DEBUG : return "DEBUG"; case LogType::INFO : return "INFO"; case LogType::WARNING : return "WARNING"; case LogType::ERROR : return "ERROR"; case LogType::FATAL : return "FATAL"; // clang-format on } return {}; // unreached } Log::Log() { enabled_map_[LogType::ERROR] = true; enabled_map_[LogType::WARNING] = true; enabled_map_[LogType::INFO] = true; enabled_map_[LogType::DEBUG] = true; enabled_map_[LogType::FATAL] = true; } Log& Log::get() { static Log log; return log; } void Log::take_input(LogType type, const std::optional& loc, std::ostream& out, std::string&& input) { auto print_out = [&]() { out << logtype_str(type) << ": " << input << std::endl; }; if (loc) { if (src_.empty()) { std::cerr << "Log: cannot resolve location before calling set_source()." << std::endl; print_out(); } else if (loc->begin.line == 0) { std::cerr << "Log: invalid location." << std::endl; print_out(); } else if (loc->begin.line > loc->end.line) { std::cerr << "Log: loc.begin > loc.end: " << loc->begin << ":" << loc->end << std::endl; print_out(); } else { log_with_location(type, loc.value(), out, input); } } else { print_out(); } } const std::string Log::get_source_line(unsigned int n) { // Get the Nth source line (N is 0-based). Return an empty string if it // doesn't exist std::string line; std::stringstream ss(src_); for (unsigned int idx = 0; idx <= n; idx++) { std::getline(ss, line); if (ss.eof() && idx == n) return line; if (!ss) return ""; } return line; } void Log::log_with_location(LogType type, const location& l, std::ostream& out, const std::string& m) { if (filename_.size()) { out << filename_ << ":"; } std::string msg(m); const std::string& typestr = logtype_str(type); if (!msg.empty() && msg.back() == '\n') { msg.pop_back(); } /* For a multi line error only the line range is printed: :-: ERROR: */ if (l.begin.line < l.end.line) { out << l.begin.line << "-" << l.end.line << ": " << typestr << ": " << msg << std::endl; return; } /* For a single line error the format is: ::-: ERROR: E.g. file.bt:1:10-20: error: i:s:1 /1 < "str"/ ~~~~~~~~~~ */ out << l.begin.line << ":" << l.begin.column << "-" << l.end.column; out << ": " << typestr << ": " << msg << std::endl; // for bpftrace::position, valid line# starts from 1 std::string srcline = get_source_line(l.begin.line - 1); if (srcline == "") return; // To get consistent printing all tabs will be replaced with 4 spaces for (auto c : srcline) { if (c == '\t') out << " "; else out << c; } out << std::endl; for (unsigned int x = 0; x < srcline.size() && x < (static_cast(l.end.column) - 1); x++) { char marker = (x < (static_cast(l.begin.column) - 1)) ? ' ' : '~'; if (srcline[x] == '\t') { out << std::string(4, marker); } else { out << marker; } } out << std::endl; } LogStream::LogStream(const std::string& file, int line, LogType type, std::ostream& out) : sink_(Log::get()), type_(type), loc_(std::nullopt), out_(out), log_file_(file), log_line_(line) { } LogStream::LogStream(const std::string& file, int line, LogType type, const location& loc, std::ostream& out) : sink_(Log::get()), type_(type), loc_(loc), out_(out), log_file_(file), log_line_(line) { } LogStream::~LogStream() { #ifdef FUZZ // When fuzzing, we don't want to output error messages. However, some // function uses a error message length to determine whether if an error // occur. So, we cannot simply DISABLE_LOG(ERROR). Instead, here, we don't // output error messages to stderr. if (sink_.is_enabled(type_) && (type_ != LogType::ERROR || (&out_ != &std::cout && &out_ != &std::cerr))) #else if (sink_.is_enabled(type_)) #endif { std::string prefix = ""; if (type_ == LogType::DEBUG) prefix = "[" + log_file_ + ":" + std::to_string(log_line_) + "] "; sink_.take_input(type_, loc_, out_, prefix + buf_.str()); } } [[noreturn]] LogStreamFatal::~LogStreamFatal() { sink_.take_input(type_, loc_, out_, buf_.str()); abort(); } }; // namespace bpftrace bpftrace-0.14.0/src/log.h000066400000000000000000000067541413460502400151160ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "location.hh" namespace bpftrace { // clang-format off enum class LogType { DEBUG, INFO, WARNING, ERROR, FATAL }; // clang-format on class Log { public: static Log& get(); void take_input(LogType type, const std::optional& loc, std::ostream& out, std::string&& input); inline void set_source(const std::string& filename, const std::string& source) { src_ = source; filename_ = filename; } inline const std::string& get_source() { return src_; } const std::string get_source_line(unsigned int n); // Can only construct with get() Log(const Log& other) = delete; Log& operator=(const Log& other) = delete; inline void enable(LogType type) { enabled_map_[type] = true; } inline void disable(LogType type) { assert(type != LogType::FATAL); enabled_map_[type] = false; } inline bool is_enabled(LogType type) { return enabled_map_[type]; } private: Log(); ~Log() = default; std::string src_; std::string filename_; void log_with_location(LogType, const location&, std::ostream&, const std::string&); std::unordered_map enabled_map_; }; 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, const location& loc, std::ostream& out = std::cerr); template LogStream& operator<<(const T& v) { if (sink_.is_enabled(type_)) buf_ << v; return *this; } virtual ~LogStream(); protected: Log& sink_; LogType type_; const std::optional loc_; std::ostream& out_; std::string log_file_; int log_line_; std::ostringstream buf_; }; class LogStreamFatal : public LogStream { public: LogStreamFatal(const std::string& file, int line, __attribute__((unused)) LogType, std::ostream& out = std::cerr) : LogStream(file, line, LogType::FATAL, out){}; LogStreamFatal(const std::string& file, int line, __attribute__((unused)) LogType, const location& loc, std::ostream& out = std::cerr) : LogStream(file, line, LogType::FATAL, loc, out){}; [[noreturn]] ~LogStreamFatal(); }; // Usage examples: // 1. LOG(WARNING) << "this is a " << "warning!"; (this goes to std::cerr) // 2. LOG(DEBUG, std::cout) << "this is a " << " message."; // 3. LOG(ERROR, call.loc, std::cerr) << "this is a semantic error"; // Note: LogType::DEBUG will prepend __FILE__ and __LINE__ to the debug message // clang-format off #define LOGSTREAM_COMMON(...) bpftrace::LogStream(__FILE__, __LINE__, __VA_ARGS__) #define LOGSTREAM_DEBUG(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_INFO(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_WARNING(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_ERROR(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_FATAL(...) bpftrace::LogStreamFatal(__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) }; // namespace bpftrace bpftrace-0.14.0/src/main.cpp000066400000000000000000000625001413460502400156030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "aot/aot.h" #include "ast/bpforc/bpforc.h" #include "ast/pass_manager.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/field_analyser.h" #include "ast/passes/node_counter.h" #include "ast/passes/portability_analyser.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpffeature.h" #include "bpftrace.h" #include "build_info.h" #include "child.h" #include "clang_parser.h" #include "driver.h" #include "lockdown.h" #include "log.h" #include "output.h" #include "probe_matcher.h" #include "procmon.h" #include "tracepoint_format_parser.h" using namespace bpftrace; namespace { enum class OutputBufferConfig { UNSET = 0, LINE, FULL, NONE, }; enum class TestMode { UNSET = 0, SEMANTIC, CODEGEN, }; enum class BuildMode { // Compile script and run immediately DYNAMIC = 0, // Compile script into portable executable AHEAD_OF_TIME, }; } // namespace void usage() { // clang-format off std::cerr << "USAGE:" << std::endl; std::cerr << " bpftrace [options] filename" << std::endl; std::cerr << " bpftrace [options] - " << std::endl; std::cerr << " bpftrace [options] -e 'program'" << std::endl; std::cerr << std::endl; std::cerr << "OPTIONS:" << std::endl; std::cerr << " -B MODE output buffering mode ('full', 'none')" << std::endl; std::cerr << " -f FORMAT output format ('text', 'json')" << std::endl; std::cerr << " -o file redirect bpftrace output to file" << std::endl; std::cerr << " -d debug info dry run" << std::endl; std::cerr << " -dd verbose debug info dry run" << std::endl; std::cerr << " -e 'program' execute this program" << std::endl; std::cerr << " -h, --help show this help message" << std::endl; std::cerr << " -I DIR add the directory to the include search path" << std::endl; std::cerr << " --include FILE add an #include file before preprocessing" << std::endl; std::cerr << " -l [search] list probes" << std::endl; std::cerr << " -p PID enable USDT probes on PID" << std::endl; std::cerr << " -c 'CMD' run CMD and enable USDT probes on resulting process" << std::endl; std::cerr << " --usdt-file-activation" << std::endl; std::cerr << " activate usdt semaphores based on file path" << std::endl; std::cerr << " --unsafe allow unsafe builtin functions" << std::endl; std::cerr << " -q keep messages quiet" << std::endl; std::cerr << " -v verbose messages" << std::endl; std::cerr << " --info Print information about kernel BPF support" << std::endl; std::cerr << " -k emit a warning when a bpf helper returns an error (except read functions)" << std::endl; std::cerr << " -kk check all bpf helper functions" << std::endl; std::cerr << " -V, --version bpftrace version" << std::endl; std::cerr << " --no-warnings disable all warning messages" << std::endl; std::cerr << std::endl; std::cerr << "ENVIRONMENT:" << std::endl; std::cerr << " BPFTRACE_STRLEN [default: 64] bytes on BPF stack per str()" << std::endl; std::cerr << " BPFTRACE_NO_CPP_DEMANGLE [default: 0] disable C++ symbol demangling" << std::endl; std::cerr << " BPFTRACE_MAP_KEYS_MAX [default: 4096] max keys in a map" << std::endl; std::cerr << " BPFTRACE_CAT_BYTES_MAX [default: 10k] maximum bytes read by cat builtin" << std::endl; std::cerr << " BPFTRACE_MAX_PROBES [default: 512] max number of probes" << std::endl; std::cerr << " BPFTRACE_LOG_SIZE [default: 1000000] log size in bytes" << std::endl; std::cerr << " BPFTRACE_PERF_RB_PAGES [default: 64] pages per CPU to allocate for ring buffer" << std::endl; std::cerr << " BPFTRACE_NO_USER_SYMBOLS [default: 0] disable user symbol resolution" << std::endl; std::cerr << " BPFTRACE_CACHE_USER_SYMBOLS [default: auto] enable user symbol cache" << std::endl; std::cerr << " BPFTRACE_VMLINUX [default: none] vmlinux path used for kernel symbol resolution" << std::endl; std::cerr << " BPFTRACE_BTF [default: none] BTF file" << std::endl; std::cerr << std::endl; std::cerr << "EXAMPLES:" << std::endl; std::cerr << "bpftrace -l '*sleep*'" << std::endl; std::cerr << " list probes containing \"sleep\"" << std::endl; std::cerr << "bpftrace -e 'kprobe:do_nanosleep { printf(\"PID %d sleeping...\\n\", pid); }'" << std::endl; std::cerr << " trace processes calling sleep" << std::endl; std::cerr << "bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'" << std::endl; std::cerr << " 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(ERROR) << 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"; } bool is_root() { if (geteuid() != 0) { LOG(ERROR) << "bpftrace currently only supports running as the root user."; return false; } else return true; } static int info() { struct utsname utsname; uname(&utsname); std::cerr << "System" << std::endl << " OS: " << utsname.sysname << " " << utsname.release << " " << utsname.version << std::endl << " Arch: " << utsname.machine << std::endl; std::cerr << std::endl; std::cerr << BuildInfo::report(); std::cerr << std::endl; std::cerr << BPFfeature().report(); return 0; } static std::optional get_boottime() { 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_REALTIME, &before)) continue; if (::clock_gettime(CLOCK_BOOTTIME, &boottime)) continue; if (::clock_gettime(CLOCK_REALTIME, &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 boottime_realtime; long nsec_avg = (before.tv_nsec + after.tv_nsec) / 2; if (nsec_avg - boottime.tv_nsec < 0) { boottime_realtime.tv_sec = after.tv_sec - boottime.tv_sec - 1; boottime_realtime.tv_nsec = nsec_avg - boottime.tv_nsec + 1e9; } else { boottime_realtime.tv_sec = after.tv_sec - boottime.tv_sec; boottime_realtime.tv_nsec = nsec_avg - boottime.tv_nsec; } lowest_delta = delta; ret = boottime_realtime; } } if (ret && lowest_delta >= 1e5) LOG(WARNING) << (lowest_delta / 1e3) << "us skew detected when calculating boot time. strftime() " "builtin may be inaccurate"; return ret; } [[nodiscard]] static bool parse_env(BPFtrace& bpftrace) { if (!get_uint64_env_var("BPFTRACE_STRLEN", bpftrace.strlen_)) return false; // in practice, the largest buffer I've seen fit into the BPF stack was 240 // bytes. I've set the bar lower, in case your program has a deeper stack than // the one from my tests, in the hope that you'll get this instructive error // instead of getting the BPF verifier's error. if (bpftrace.strlen_ > 200) { // the verifier errors you would encounter when attempting larger // allocations would be: >240= ~1024= LOG(ERROR) << "'BPFTRACE_STRLEN' " << bpftrace.strlen_ << " exceeds the current maximum of 200 bytes.\n" << "This limitation is because strings are currently stored on " "the 512 byte BPF stack.\n" << "Long strings will be pursued in: " "https://github.com/iovisor/bpftrace/issues/305"; return false; } if (const char* env_p = std::getenv("BPFTRACE_NO_CPP_DEMANGLE")) { if (std::string(env_p) == "1") bpftrace.demangle_cpp_symbols_ = false; else if (std::string(env_p) == "0") bpftrace.demangle_cpp_symbols_ = true; else { LOG(ERROR) << "Env var 'BPFTRACE_NO_CPP_DEMANGLE' did not contain a " "valid value (0 or 1)."; return false; } } if (!get_uint64_env_var("BPFTRACE_MAP_KEYS_MAX", bpftrace.mapmax_)) return false; if (!get_uint64_env_var("BPFTRACE_MAX_PROBES", bpftrace.max_probes_)) return false; if (!get_uint64_env_var("BPFTRACE_LOG_SIZE", bpftrace.log_size_)) return false; if (!get_uint64_env_var("BPFTRACE_PERF_RB_PAGES", bpftrace.perf_rb_pages_)) return false; if (!get_uint64_env_var("BPFTRACE_MAX_TYPE_RES_ITERATIONS", bpftrace.max_type_res_iterations)) return 1; if (!get_uint64_env_var("BPFTRACE_MAX_TYPE_RES_ITERATIONS", bpftrace.max_type_res_iterations)) return false; if (const char* env_p = std::getenv("BPFTRACE_CAT_BYTES_MAX")) { uint64_t proposed; std::istringstream stringstream(env_p); if (!(stringstream >> proposed)) { LOG(ERROR) << "Env var 'BPFTRACE_CAT_BYTES_MAX' did not contain a valid " "uint64_t, or was zero-valued."; return false; } bpftrace.cat_bytes_max_ = proposed; } if (const char* env_p = std::getenv("BPFTRACE_NO_USER_SYMBOLS")) { std::string s(env_p); if (s == "1") bpftrace.resolve_user_symbols_ = false; else if (s == "0") bpftrace.resolve_user_symbols_ = true; else { LOG(ERROR) << "Env var 'BPFTRACE_NO_USER_SYMBOLS' did not contain a " "valid value (0 or 1)."; return false; } } if (const char* env_p = std::getenv("BPFTRACE_CACHE_USER_SYMBOLS")) { std::string s(env_p); if (s == "1") bpftrace.cache_user_symbols_ = true; else if (s == "0") bpftrace.cache_user_symbols_ = false; else { LOG(ERROR) << "Env var 'BPFTRACE_CACHE_USER_SYMBOLS' did not contain a " "valid value (0 or 1)."; return false; } } else { // enable user symbol cache if ASLR is disabled on system or `-c` option is // given bpftrace.cache_user_symbols_ = !bpftrace.cmd_.empty() || !bpftrace.is_aslr_enabled(-1); } uint64_t node_max = std::numeric_limits::max(); if (!get_uint64_env_var("BPFTRACE_NODE_MAX", node_max)) return false; bpftrace.ast_max_nodes_ = node_max; return true; } [[nodiscard]] std::unique_ptr parse( BPFtrace& bpftrace, const std::string& name, const std::string& program, const std::vector& include_dirs, const std::vector& include_files) { Driver driver(bpftrace); driver.source(name, program); int err; err = driver.parse(); if (err) return nullptr; ast::FieldAnalyser fields(driver.root_, bpftrace); err = fields.analyse(); if (err) return nullptr; if (TracepointFormatParser::parse(driver.root_, bpftrace) == false) return nullptr; ClangParser clang; std::vector extra_flags; { struct utsname utsname; uname(&utsname); std::string ksrc, kobj; auto kdirs = get_kernel_dirs(utsname, !bpftrace.feature_->has_btf()); ksrc = std::get<0>(kdirs); kobj = std::get<1>(kdirs); if (ksrc != "") extra_flags = get_kernel_cflags(utsname.machine, ksrc, kobj); } extra_flags.push_back("-include"); extra_flags.push_back(CLANG_WORKAROUNDS_H); for (auto dir : include_dirs) { extra_flags.push_back("-I"); extra_flags.push_back(dir); } for (auto file : include_files) { extra_flags.push_back("-include"); extra_flags.push_back(file); } // NOTE(mmarchini): if there are no C definitions, clang parser won't run to // avoid issues in some versions. Since we're including files in the command // line, we want to force parsing, so we make sure C definitions are not // empty before going to clang parser stage. if (!include_files.empty() && driver.root_->c_definitions.empty()) driver.root_->c_definitions = "#define __BPFTRACE_DUMMY__"; if (!clang.parse(driver.root_, bpftrace, extra_flags)) return nullptr; err = driver.parse(); if (err) return nullptr; auto ast = driver.root_; driver.root_ = nullptr; return std::unique_ptr(ast); } ast::PassManager CreateDynamicPM() { ast::PassManager pm; pm.AddPass(ast::CreateSemanticPass()); pm.AddPass(ast::CreateCounterPass()); pm.AddPass(ast::CreateResourcePass()); return pm; } ast::PassManager CreateAotPM(std::string __attribute__((unused))) { ast::PassManager pm; pm.AddPass(ast::CreateSemanticPass()); pm.AddPass(ast::CreatePortabilityPass()); pm.AddPass(ast::CreateResourcePass()); return pm; } int main(int argc, char* argv[]) { int err; std::string pid_str; std::string cmd_str; bool listing = false; bool safe_mode = true; bool usdt_file_activation = false; int helper_check_level = 0; TestMode test_mode = TestMode::UNSET; std::string script, search, file_name, output_file, output_format, output_elf, aot; OutputBufferConfig obc = OutputBufferConfig::UNSET; BuildMode build_mode = BuildMode::DYNAMIC; int c; const char* const short_options = "dbB:f:e:hlp:vqc:Vo:I:k"; option long_options[] = { option{ "help", no_argument, nullptr, 'h' }, option{ "version", no_argument, nullptr, 'V' }, option{ "usdt-file-activation", no_argument, nullptr, '$' }, option{ "unsafe", no_argument, nullptr, 'u' }, option{ "btf", no_argument, nullptr, 'b' }, option{ "include", required_argument, nullptr, '#' }, option{ "info", no_argument, nullptr, 2000 }, option{ "emit-elf", required_argument, nullptr, 2001 }, option{ "no-warnings", no_argument, nullptr, 2002 }, option{ "test", required_argument, nullptr, 2003 }, option{ "aot", required_argument, nullptr, 2004 }, option{ nullptr, 0, nullptr, 0 }, // Must be last }; std::vector include_dirs; std::vector include_files; while ((c = getopt_long( argc, argv, short_options, long_options, nullptr)) != -1) { switch (c) { case 2000: // --info if (is_root()) return info(); return 1; break; case 2001: // --emit-elf output_elf = optarg; break; case 2002: // --no-warnings DISABLE_LOG(WARNING); break; case 2003: // --test if (std::strcmp(optarg, "semantic") == 0) test_mode = TestMode::SEMANTIC; else if (std::strcmp(optarg, "codegen") == 0) test_mode = TestMode::CODEGEN; else { LOG(ERROR) << "USAGE: --test must be either 'semantic' or 'codegen'."; return 1; } break; case 2004: // --aot aot = optarg; build_mode = BuildMode::AHEAD_OF_TIME; break; case 'o': output_file = optarg; break; case 'd': bt_debug++; if (bt_debug == DebugLevel::kNone) { usage(); return 1; } break; case 'q': bt_quiet = true; break; case 'v': bt_verbose = true; break; case 'B': if (std::strcmp(optarg, "line") == 0) { obc = OutputBufferConfig::LINE; } else if (std::strcmp(optarg, "full") == 0) { obc = OutputBufferConfig::FULL; } else if (std::strcmp(optarg, "none") == 0) { obc = OutputBufferConfig::NONE; } else { LOG(ERROR) << "USAGE: -B must be either 'line', 'full', or 'none'."; return 1; } break; case 'f': output_format = optarg; break; case 'e': script = optarg; break; case 'p': pid_str = optarg; break; case 'I': include_dirs.push_back(optarg); break; case '#': include_files.push_back(optarg); break; case 'l': listing = true; break; case 'c': cmd_str = optarg; break; case '$': usdt_file_activation = true; break; case 'u': safe_mode = false; break; case 'b': break; case 'h': usage(); return 0; case 'V': std::cout << "bpftrace " << BPFTRACE_VERSION << std::endl; return 0; case 'k': helper_check_level++; if (helper_check_level >= 3) { usage(); return 1; } break; default: usage(); return 1; } } if (argc == 1) { usage(); return 1; } if (bt_verbose && (bt_debug != DebugLevel::kNone)) { // TODO: allow both LOG(ERROR) << "USAGE: Use either -v or -d."; return 1; } if (!cmd_str.empty() && !pid_str.empty()) { LOG(ERROR) << "USAGE: Cannot use both -c and -p."; usage(); return 1; } 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") { 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; } switch (obc) { case OutputBufferConfig::UNSET: case OutputBufferConfig::LINE: std::setvbuf(stdout, NULL, _IOLBF, BUFSIZ); break; case OutputBufferConfig::FULL: std::setvbuf(stdout, NULL, _IOFBF, BUFSIZ); break; case OutputBufferConfig::NONE: std::setvbuf(stdout, NULL, _IONBF, BUFSIZ); break; } BPFtrace bpftrace(std::move(output)); if (!cmd_str.empty()) bpftrace.cmd_ = cmd_str; if (!parse_env(bpftrace)) return 1; // Difficult to serialize flex generated types if (helper_check_level && build_mode == BuildMode::AHEAD_OF_TIME) { LOG(ERROR) << "Cannot use -k[k] with --aot"; return 1; } bpftrace.usdt_file_activation_ = usdt_file_activation; bpftrace.safe_mode_ = safe_mode; bpftrace.helper_check_level_ = helper_check_level; bpftrace.boottime_ = get_boottime(); if (!pid_str.empty()) { try { bpftrace.procmon_ = std::make_unique(pid_str); } catch (const std::exception& e) { LOG(ERROR) << e.what(); return 1; } } if (!cmd_str.empty()) { bpftrace.cmd_ = cmd_str; try { bpftrace.child_ = std::make_unique(cmd_str); } catch (const std::runtime_error& e) { LOG(ERROR) << "Failed to fork child: " << e.what(); return -1; } } // Listing probes if (listing) { if (!is_root()) return 1; if (optind == argc || std::string(argv[optind]) == "*") script = "*:*"; else if (optind == argc - 1) script = argv[optind]; else { usage(); return 1; } if (script.find(':') == std::string::npos && (script.find("struct") == 0 || script.find("union") == 0 || script.find("enum") == 0)) { // Print structure definitions bpftrace.probe_matcher_->list_structs(script); return 0; } Driver driver(bpftrace); driver.listing_ = true; driver.source("stdin", script); int err = driver.parse(); if (err) return err; ast::SemanticAnalyser semantics(driver.root_, bpftrace, false, true); err = semantics.analyse(); if (err) return err; bpftrace.probe_matcher_->list_probes(driver.root_); return 0; } std::string filename; std::string program; if (script.empty()) { // Script file if (argv[optind] == nullptr) { LOG(ERROR) << "USAGE: filename or -e 'program' required."; return 1; } filename = argv[optind]; std::stringstream buf; if (filename == "-") { std::string line; while (std::getline(std::cin, line)) { // Note we may add an extra newline if the input doesn't end in a new // line. This should not matter because bpftrace (the language) is not // whitespace sensitive. buf << line << std::endl; } filename = "stdin"; program = buf.str(); } else { std::ifstream file(filename); if (file.fail()) { LOG(ERROR) << "failed to open file '" << filename << "': " << std::strerror(errno); return -1; } program = buf.str(); buf << file.rdbuf(); program = buf.str(); } optind++; } else { // Script is provided as a command line argument filename = "stdin"; program = script; } // Load positional parameters before driver runs so positional // parameters used inside attach point definitions can be resolved. while (optind < argc) { bpftrace.add_param(argv[optind]); optind++; } if (!is_root()) return 1; auto lockdown_state = lockdown::detect(bpftrace.feature_); if (lockdown_state == lockdown::LockdownState::Confidentiality) { lockdown::emit_warning(std::cerr); return 1; } // FIXME (mmarchini): maybe we don't want to always enforce an infinite // rlimit? enforce_infinite_rlimit(); auto ast_root = parse( bpftrace, filename, program, include_dirs, include_files); if (!ast_root) return 1; ast::PassContext ctx(bpftrace); ast::PassManager pm; switch (build_mode) { case BuildMode::DYNAMIC: pm = CreateDynamicPM(); break; case BuildMode::AHEAD_OF_TIME: pm = CreateAotPM(aot); break; } auto pmresult = pm.Run(std::move(ast_root), ctx); if (!pmresult.Ok()) return 1; ast_root = std::unique_ptr(pmresult.Root()); if (!bpftrace.cmd_.empty()) { try { bpftrace.child_ = std::make_unique(cmd_str); } catch (const std::runtime_error& e) { LOG(ERROR) << "Failed to fork child: " << e.what(); return -1; } } ast::CodegenLLVM llvm(&*ast_root, bpftrace); BpfBytecode bytecode; try { std::unique_ptr bpforc; llvm.generate_ir(); if (bt_debug == DebugLevel::kFullDebug) { std::cout << "Before optimization\n"; std::cout << "-------------------\n\n"; llvm.DumpIR(); } llvm.optimize(); if (bt_debug != DebugLevel::kNone) { if (bt_debug == DebugLevel::kFullDebug) { std::cout << "\nAfter optimization\n"; std::cout << "------------------\n\n"; } llvm.DumpIR(); } if (!output_elf.empty()) { llvm.emit_elf(output_elf); return 0; } bpforc = llvm.emit(); if (bt_debug == DebugLevel::kFullDebug) { std::cout << "\nLLVM JITDLib state\n"; std::cout << "------------------\n\n"; raw_os_ostream os(std::cout); bpforc->dump(os); } bytecode = bpforc->getBytecode(); } catch (const std::system_error& ex) { LOG(ERROR) << "failed to write elf: " << ex.what(); return 1; } catch (const std::exception& ex) { LOG(ERROR) << "Failed to compile: " << ex.what(); return 1; } if (bt_debug != DebugLevel::kNone || test_mode == TestMode::CODEGEN) return 0; if (build_mode == BuildMode::AHEAD_OF_TIME) return aot::generate(bpftrace.resources, bytecode, aot); // 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, NULL); sigaction(SIGTERM, &act, NULL); err = bpftrace.run(bytecode); if (err) return err; // We are now post-processing. If we receive another SIGINT, // handle it normally (exit) act.sa_handler = SIG_DFL; sigaction(SIGINT, &act, NULL); std::cout << "\n\n"; err = bpftrace.print_maps(); if (bt_verbose && bpftrace.child_) { auto val = 0; if ((val = bpftrace.child_->term_signal()) > -1) std::cout << "Child terminated by signal: " << val << std::endl; if ((val = bpftrace.child_->exit_code()) > -1) std::cout << "Child exited with code: " << val << std::endl; } if (err) return err; return 0; } bpftrace-0.14.0/src/map.cpp000066400000000000000000000150351413460502400154350ustar00rootroot00000000000000#include #include #include #include "bpftrace.h" #include "log.h" #include "utils.h" #include #include "map.h" #include "mapmanager.h" namespace bpftrace { namespace { int create_map(enum bpf_map_type map_type, const std::string &name, int key_size, int value_size, int max_entries, int flags) { std::string fixed_name; const std::string *name_ptr = &name; if (!name.empty() && name[0] == '@') { fixed_name = "AT_" + name.substr(1); name_ptr = &fixed_name; } #ifdef HAVE_BCC_CREATE_MAP return bcc_create_map( map_type, name_ptr->c_str(), key_size, value_size, max_entries, flags); #else return bpf_create_map( map_type, name_ptr->c_str(), key_size, value_size, max_entries, flags); #endif } } // namespace Map::Map(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step, int max_entries) : IMap(name, type, key, min, max, step, max_entries) { int key_size = key.size(); if (type.IsHistTy() || type.IsLhistTy() || type.IsAvgTy() || type.IsStatsTy()) key_size += 8; if (key_size == 0) key_size = 8; if (type.IsCountTy() && !key.args_.size()) { max_entries = 1; key_size = 4; } int value_size = type.GetSize(); int flags = 0; mapfd_ = create_map( map_type_, name, key_size, value_size, max_entries, flags); if (mapfd_ < 0) { LOG(ERROR) << "failed to create map: '" << name_ << "': " << strerror(errno); } } Map::Map(const std::string &name, enum bpf_map_type type, int key_size, int value_size, int max_entries, int flags) : IMap(name, type, key_size, value_size, max_entries, flags) { mapfd_ = create_map( map_type_, name_, key_size, value_size, max_entries, flags); if (mapfd_ < 0) { LOG(ERROR) << "failed to create map: '" << name_ << "': " << strerror(errno); } } // bpf_get_stackid() is kind of broken by design. // // First, stacks can suffer from hash collisions and we can lose stacks. We // receive collision errors in userspace b/c we do not set BPF_F_REUSE_STACKID. // Note that we should **NEVER** set this flag b/c then we can silently receive // unrelated stacks in userspace. // // Second, there is not a great way to reduce the frequency of collision (other // than increasing map size) b/c multiple probes can be sharing the same stack // (remember they are hashed). As a result, we cannot delete entries in this // map b/c we don't know when all the sharers are gone. // // Fortunately, we haven't seen many bug reports about missing stacks (or maybe // people don't care that much). The proper solution would be to use // bpf_get_stack() to get the actual stack trace and pass it to userspace // through the ring buffer. However, there is additional complexity with this // b/c we need to set up a percpu map to use as scratch space for // bpf_get_stack() to write into. Using the stack directly wouldn't work well // b/c it would take too much stack space. // // The temporary fix is to bump the map size to 128K. Any futher bumps should // warrant consideration of the previous paragraph. Map::Map(const SizedType &type) : IMap(type) { int key_size = 4; int value_size = sizeof(uintptr_t) * type.stack_type.limit; int max_entries = 128 << 10; int flags = 0; mapfd_ = create_map( map_type_, "stack", key_size, value_size, max_entries, flags); if (mapfd_ < 0) { LOG(ERROR) << "failed to create stack id map\n" // TODO (mmarchini): Check perf_event_max_stack in the semantic_analyzer << "This might have happened because kernel.perf_event_max_stack " << "is smaller than " << type.stack_type.limit << ". Try to tweak this value with " << "sysctl kernel.perf_event_max_stack="; } } Map::Map(enum bpf_map_type map_type) : IMap(map_type) { std::vector cpus = get_online_cpus(); int key_size = 4; int value_size = 4; int max_entries = cpus.size(); int flags = 0; mapfd_ = create_map( map_type, "printf", key_size, value_size, max_entries, flags); if (mapfd_ < 0) { LOG(ERROR) << "failed to create " << name_ << " map: " << strerror(errno); } } Map::~Map() { if (mapfd_ >= 0) close(mapfd_); } void MapManager::Add(std::unique_ptr map) { auto name = map->name_; if (name.empty()) throw std::runtime_error("Map without name cannot be stored"); auto count = maps_by_name_.count(name); if (count > 0) throw std::runtime_error("Map with name: @" + name + " already exists"); auto id = maps_by_id_.size(); map->id = id; maps_by_name_[name] = map.get(); maps_by_id_.emplace_back(std::move(map)); } bool MapManager::Has(const std::string &name) { return maps_by_name_.find(name) != maps_by_name_.end(); } std::optional MapManager::Lookup(const std::string &name) { auto search = maps_by_name_.find(name); if (search == maps_by_name_.end()) { return {}; } return search->second; } std::optional MapManager::Lookup(ssize_t id) { if (id >= (ssize_t)maps_by_id_.size()) return {}; return maps_by_id_[id].get(); } void MapManager::Set(MapManager::Type t, std::unique_ptr map) { auto id = maps_by_id_.size(); map->id = id; map->printable_ = false; maps_by_type_[t] = map.get(); maps_by_id_.emplace_back(std::move(map)); } std::optional MapManager::Lookup(Type t) { auto search = maps_by_type_.find(t); if (search == maps_by_type_.end()) { return {}; } return search->second; } bool MapManager::Has(Type t) { return maps_by_type_.find(t) != maps_by_type_.end(); } void MapManager::Set(StackType t, std::unique_ptr map) { auto id = maps_by_id_.size(); map->id = id; map->printable_ = false; stackid_maps_[t] = map.get(); maps_by_id_.emplace_back(std::move(map)); } std::optional MapManager::Lookup(StackType t) { auto search = stackid_maps_.find(t); if (search == stackid_maps_.end()) { return {}; } return search->second; } bool MapManager::Has(StackType t) { return stackid_maps_.find(t) != stackid_maps_.end(); } std::string to_string(MapManager::Type t) { switch (t) { case MapManager::Type::PerfEvent: return "perf_event"; case MapManager::Type::Join: return "join"; case MapManager::Type::Elapsed: return "elapsed"; case MapManager::Type::SeqPrintfData: return "seq_printf_data"; } return {}; // unreached } } // namespace bpftrace bpftrace-0.14.0/src/map.h000066400000000000000000000012351413460502400150770ustar00rootroot00000000000000#pragma once #include "imap.h" namespace bpftrace { class Map : public IMap { public: Map(const std::string &name, const SizedType &type, const MapKey &key, int max_entries) : Map(name, type, key, 0, 0, 0, max_entries){}; Map(const std::string &name, const SizedType &type, const MapKey &key, int min, int max, int step, int max_entries); Map(const std::string &name, enum bpf_map_type type, int key_size, int value_size, int max_entries, int flags); Map(const SizedType &type); Map(enum bpf_map_type map_type); virtual ~Map() override; }; } // namespace bpftrace bpftrace-0.14.0/src/mapkey.cpp000066400000000000000000000102571413460502400161470ustar00rootroot00000000000000#include #include "async_event_types.h" #include "bpftrace.h" #include "log.h" #include "mapkey.h" #include "utils.h" namespace bpftrace { bool MapKey::operator!=(const MapKey &k) const { return args_ != k.args_; } size_t MapKey::size() const { size_t size = 0; for (auto &arg : args_) size += arg.GetSize(); return size; } std::string MapKey::argument_type_list() const { std::ostringstream list; list << "["; for (size_t i = 0; i < args_.size(); i++) { if (i) list << ", "; list << args_[i]; } list << "]"; return list.str(); } std::vector MapKey::argument_value_list(BPFtrace &bpftrace, const std::vector &data) const { std::vector list; int offset = 0; for (const SizedType &arg : args_) { list.push_back(argument_value(bpftrace, arg, &data[offset])); offset += arg.GetSize(); } return list; } std::string MapKey::argument_value_list_str(BPFtrace &bpftrace, const std::vector &data) const { if (args_.empty()) return ""; return "[" + str_join(argument_value_list(bpftrace, data), ", ") + "]"; } std::string MapKey::argument_value(BPFtrace &bpftrace, const SizedType &arg, const void *data) { auto arg_data = static_cast(data); std::ostringstream ptr; switch (arg.type) { case Type::integer: switch (arg.GetSize()) { case 1: return std::to_string(read_data(data)); case 2: return std::to_string(read_data(data)); case 4: return std::to_string(read_data(data)); case 8: return std::to_string(read_data(data)); default: break; } break; case Type::kstack: return bpftrace.get_stack( read_data(data), false, arg.stack_type, 4); case Type::ustack: return bpftrace.get_stack( read_data(data), true, arg.stack_type, 4); case Type::timestamp: { auto p = static_cast(data); return bpftrace.resolve_timestamp(p->strftime_id, p->nsecs_since_boot); } case Type::ksym: return bpftrace.resolve_ksym(read_data(data)); case Type::usym: return bpftrace.resolve_usym(read_data(data), read_data(arg_data + 8)); case Type::inet: return bpftrace.resolve_inet(read_data(data), (const uint8_t *)(arg_data + 8)); case Type::username: return bpftrace.resolve_uid(read_data(data)); case Type::probe: return bpftrace.resources.probe_ids[read_data(data)]; case Type::string: { auto p = static_cast(data); return std::string(p, strnlen(p, arg.GetSize())); } case Type::buffer: { auto p = static_cast(data) + 1; return hex_format_buffer(p, arg.GetSize() - 1); } case Type::pointer: { // use case: show me these pointer values ptr << "0x" << std::hex << read_data(data); return ptr.str(); } case Type::array: { std::vector elems; for (size_t i = 0; i < arg.GetNumElements(); i++) elems.push_back(argument_value(bpftrace, *arg.GetElementTy(), (const uint8_t *)data + i * arg.GetElementTy()->GetSize())); return "[" + str_join(elems, ",") + "]"; } case Type::record: { std::vector elems; for (auto &field : arg.GetFields()) { elems.push_back("." + field.name + "=" + argument_value(bpftrace, field.type, (const uint8_t *)data + field.offset)); } return "{" + str_join(elems, ",") + "}"; } case Type::mac_address: { auto p = static_cast(data); return bpftrace.resolve_mac_address(p); } default: LOG(ERROR) << "invalid mapkey argument type"; } abort(); } } // namespace bpftrace bpftrace-0.14.0/src/mapkey.h000066400000000000000000000015761413460502400156200ustar00rootroot00000000000000#pragma once #include #include #include #include "types.h" namespace bpftrace { class BPFtrace; class MapKey { public: std::vector args_; bool operator!=(const MapKey &k) const; size_t size() const; std::string argument_type_list() const; std::vector argument_value_list( BPFtrace &bpftrace, const std::vector &data) const; std::string argument_value_list_str(BPFtrace &bpftrace, const std::vector &data) const; private: static std::string argument_value(BPFtrace &bpftrace, const SizedType &arg, const void *data); friend class cereal::access; template void serialize(Archive &archive) { archive(args_); } }; } // namespace bpftrace bpftrace-0.14.0/src/mapmanager.h000066400000000000000000000036561413460502400164430ustar00rootroot00000000000000#pragma once #include "imap.h" #include #include #include #include namespace bpftrace { /** A container for all maps created during program execution. */ class MapManager { public: MapManager() = default; MapManager &operator=(MapManager &&) = default; MapManager(const MapManager &) = delete; MapManager &operator=(const MapManager &) = delete; MapManager(MapManager &&) = delete; /** Store and lookup named maps */ void Add(std::unique_ptr map); bool Has(const std::string &name); std::optional Lookup(const std::string &name); std::optional Lookup(ssize_t id); std::optional operator[](ssize_t id) { return Lookup(id); }; std::optional operator[](const std::string &name) { return Lookup(name); }; /** Iterate over named maps, vector like iteration */ auto begin() { return maps_by_id_.begin(); }; auto end() { return maps_by_id_.end(); }; /** Internal maps */ enum class Type { // Also update to_string PerfEvent, Join, Elapsed, SeqPrintfData, }; void Set(Type t, std::unique_ptr map); std::optional Lookup(Type t); std::optional operator[](Type t) { return Lookup(t); }; bool Has(Type t); /** Stack maps */ void Set(StackType t, std::unique_ptr map); std::optional Lookup(StackType t); std::optional operator[](StackType t) { return Lookup(t); }; bool Has(StackType t); ssize_t CountStackTypes() { return stackid_maps_.size(); }; private: // `maps_by_id_` holds *all* maps std::vector> maps_by_id_; std::unordered_map maps_by_name_; std::unordered_map maps_by_type_; std::unordered_map stackid_maps_; }; std::string to_string(MapManager::Type t); } // namespace bpftrace bpftrace-0.14.0/src/output.cpp000066400000000000000000000573521413460502400162300ustar00rootroot00000000000000#include "output.h" #include "bpftrace.h" #include "log.h" #include "utils.h" #include namespace bpftrace { namespace { bool is_quoted_type(const SizedType &ty) { return ty.IsKstackTy() || ty.IsUstackTy() || ty.IsKsymTy() || ty.IsUsymTy() || ty.IsInetTy() || ty.IsUsernameTy() || ty.IsStringTy() || ty.IsBufferTy() || ty.IsProbeTy(); } } // namespace std::ostream& operator<<(std::ostream& out, MessageType type) { switch (type) { case MessageType::map: out << "map"; break; case MessageType::value: out << "value"; break; case MessageType::hist: out << "hist"; break; case MessageType::stats: out << "stats"; break; case MessageType::printf: out << "printf"; break; case MessageType::time: out << "time"; break; case MessageType::cat: out << "cat"; break; case MessageType::join: out << "join"; break; case MessageType::syscall: out << "syscall"; break; case MessageType::attached_probes: out << "attached_probes"; break; case MessageType::lost_events: out << "lost_events"; break; default: out << "?"; } return out; } std::string TextOutput::hist_index_label(int power) { char suffix = '\0'; if (power >= 40) { suffix = 'T'; power -= 40; } else if (power >= 30) { suffix = 'G'; power -= 30; } else if (power >= 20) { suffix = 'M'; power -= 20; } else if (power >= 10) { suffix = 'K'; power -= 10; } std::ostringstream label; label << (1< &values, int &min_index, int &max_index, int &max_value) const { min_index = -1; max_index = -1; max_value = 0; for (size_t i = 0; i < values.size(); i++) { int v = values.at(i); if (v > 0) { if (min_index == -1) min_index = i; max_index = i; } if (v > max_value) max_value = v; } } void Output::lhist_prepare(const std::vector &values, int min, int max, int step, int &max_index, int &max_value, int &buckets, int &start_value, int &end_value) const { max_index = -1; max_value = 0; buckets = (max - min) / step; // excluding lt and gt buckets for (size_t i = 0; i < values.size(); i++) { int v = values.at(i); if (v != 0) max_index = i; if (v > max_value) max_value = v; } if (max_index == -1) return; // trim empty values start_value = -1; end_value = 0; for (unsigned int i = 0; i <= static_cast(buckets) + 1; i++) { if (values.at(i) > 0) { if (start_value == -1) { start_value = i; } end_value = i; } } if (start_value == -1) { start_value = 0; } } std::string Output::value_to_str(BPFtrace &bpftrace, const SizedType &type, std::vector &value, bool is_per_cpu, uint32_t div) const { uint32_t nvalues = is_per_cpu ? bpftrace.ncpus_ : 1; if (type.IsKstackTy()) return bpftrace.get_stack( read_data(value.data()), false, type.stack_type, 8); else if (type.IsUstackTy()) return bpftrace.get_stack( read_data(value.data()), true, type.stack_type, 8); else if (type.IsKsymTy()) return bpftrace.resolve_ksym(read_data(value.data())); else if (type.IsUsymTy()) return bpftrace.resolve_usym(read_data(value.data()), read_data(value.data() + 8)); else if (type.IsInetTy()) return bpftrace.resolve_inet(read_data(value.data()), (uint8_t *)(value.data() + 8)); else if (type.IsUsernameTy()) return bpftrace.resolve_uid(read_data(value.data())); else if (type.IsBufferTy()) return bpftrace.resolve_buf(reinterpret_cast(value.data() + 1), *reinterpret_cast(value.data())); else if (type.IsStringTy()) { auto p = reinterpret_cast(value.data()); return std::string(p, strnlen(p, type.GetSize())); } else if (type.IsArrayTy()) { size_t elem_size = type.GetElementTy()->GetSize(); std::vector elems; for (size_t i = 0; i < type.GetNumElements(); i++) { std::vector elem_data(value.begin() + i * elem_size, value.begin() + (i + 1) * elem_size); elems.push_back(value_to_str( bpftrace, *type.GetElementTy(), elem_data, is_per_cpu, div)); } return array_to_str(elems); } else if (type.IsRecordTy()) { std::vector elems; for (auto &field : type.GetFields()) { std::vector elem_data(value.begin() + field.offset, value.begin() + field.offset + field.type.GetSize()); elems.push_back(field_to_str( field.name, value_to_str(bpftrace, field.type, elem_data, is_per_cpu, div))); } return struct_to_str(elems); } else if (type.IsTupleTy()) { std::vector elems; for (auto &field : type.GetFields()) { std::vector elem_data(value.begin() + field.offset, value.begin() + field.offset + field.type.GetSize()); elems.push_back( value_to_str(bpftrace, field.type, elem_data, is_per_cpu, div)); } return tuple_to_str(elems); } else if (type.IsCountTy()) return std::to_string(reduce_value(value, nvalues) / div); else if (type.IsIntTy()) { auto sign = type.IsSigned(); switch (type.GetIntBitWidth()) { // clang-format off case 64: if (sign) return std::to_string(reduce_value(value, nvalues) / (int64_t)div); return std::to_string(reduce_value(value, nvalues) / div); case 32: if (sign) return std::to_string( reduce_value(value, nvalues) / (int32_t)div); return std::to_string(reduce_value(value, nvalues) / div); case 16: if (sign) return std::to_string( reduce_value(value, nvalues) / (int16_t)div); return std::to_string(reduce_value(value, nvalues) / div); case 8: if (sign) return std::to_string( reduce_value(value, nvalues) / (int8_t)div); return std::to_string(reduce_value(value, nvalues) / div); // clang-format on default: LOG(FATAL) << "value_to_str: Invalid int bitwidth: " << type.GetIntBitWidth() << "provided"; return {}; } // lgtm[cpp/missing-return] } else if (type.IsSumTy() || type.IsIntTy()) { if (type.IsSigned()) return std::to_string(reduce_value(value, nvalues) / div); return std::to_string(reduce_value(value, nvalues) / div); } else if (type.IsMinTy()) return std::to_string(min_value(value, nvalues) / div); else if (type.IsMaxTy()) return std::to_string(max_value(value, nvalues) / div); else if (type.IsProbeTy()) return bpftrace.resolve_probe(read_data(value.data())); else if (type.IsTimestampTy()) return bpftrace.resolve_timestamp( reinterpret_cast(value.data())->strftime_id, reinterpret_cast(value.data()) ->nsecs_since_boot); else if (type.IsMacAddressTy()) return bpftrace.resolve_mac_address(value.data()); else return std::to_string(read_data(value.data()) / div); } std::string Output::array_to_str(const std::vector &elems) const { return "[" + str_join(elems, ",") + "]"; } std::string Output::struct_to_str(const std::vector &elems) const { return "{ " + str_join(elems, ", ") + " }"; } std::string Output::map_to_str( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { std::vector elems; uint32_t i = 0; size_t total = values_by_key.size(); for (auto &pair : values_by_key) { auto key = pair.first; auto value = pair.second; if (top) { if (total > top && i++ < (total - top)) continue; } auto key_str = map_key_to_str(bpftrace, map, key); auto value_str = value_to_str( bpftrace, map.type_, value, map.is_per_cpu_type(), div); elems.push_back(map_keyval_to_str(map, key_str, value_str)); } return str_join(elems, map_elem_delim_to_str(map)); } std::string Output::map_hist_to_str( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const { std::vector elems; uint32_t i = 0; for (auto &key_count : total_counts_by_key) { auto &key = key_count.first; auto &value = values_by_key.at(key); if (top && values_by_key.size() > top && i++ < (values_by_key.size() - top)) continue; auto key_str = map_key_to_str(bpftrace, map, key); auto val_str = map.type_.IsHistTy() ? hist_to_str(value, div) : lhist_to_str(value, map.lqmin, map.lqmax, map.lqstep); elems.push_back(map_keyval_to_str(map, key_str, val_str)); } return str_join(elems, map_elem_delim_to_str(map)); } std::string Output::map_stats_to_str( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, int64_t>> &total_counts_by_key) const { std::vector elems; uint32_t i = 0; for (auto &key_count : total_counts_by_key) { auto &key = key_count.first; auto &value = values_by_key.at(key); if (map.type_.IsAvgTy() && top && values_by_key.size() > top && i++ < (values_by_key.size() - top)) continue; auto key_str = map_key_to_str(bpftrace, map, key); int64_t count = (int64_t)value.at(0); int64_t total = value.at(1); int64_t average = 0; if (count != 0) average = total / count; std::string value_str; if (map.type_.IsStatsTy()) { std::vector> stats = { { "count", count }, { "average", average }, { "total", total } }; value_str = key_value_pairs_to_str(stats); } else value_str = std::to_string(average / div); elems.push_back(map_keyval_to_str(map, key_str, value_str)); } return str_join(elems, map_elem_delim_to_str(map)); } void TextOutput::map( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { out_ << map_to_str(bpftrace, map, top, div, values_by_key); out_ << std::endl; } std::string TextOutput::hist_to_str(const std::vector &values, uint32_t div) const { int min_index, max_index, max_value; hist_prepare(values, min_index, max_index, max_value); if (max_index == -1) return ""; std::ostringstream res; for (int i = min_index; i <= max_index; i++) { std::ostringstream header; if (i == 0) { header << "(..., 0)"; } else if (i == 1) { header << "[0]"; } else if (i == 2) { header << "[1]"; } else { header << "[" << hist_index_label(i-2); header << ", " << hist_index_label(i-2+1) << ")"; } int max_width = 52; int bar_width = values.at(i)/(float)max_value*max_width; std::string bar(bar_width, '@'); res << std::setw(16) << std::left << header.str() << std::setw(8) << std::right << (values.at(i) / div) << " |" << std::setw(max_width) << std::left << bar << "|" << std::endl; } return res.str(); } std::string TextOutput::lhist_to_str(const std::vector &values, int min, int max, int step) const { int max_index, max_value, buckets, start_value, end_value; lhist_prepare(values, min, max, step, max_index, max_value, buckets, start_value, end_value); if (max_index == -1) return ""; std::ostringstream res; for (int i = start_value; i <= end_value; i++) { int max_width = 52; int bar_width = values.at(i)/(float)max_value*max_width; std::ostringstream header; if (i == 0) { header << "(..., " << lhist_index_label(min) << ")"; } else if (i == (buckets + 1)) { header << "[" << lhist_index_label(max) << ", ...)"; } else { header << "[" << lhist_index_label((i - 1) * step + min); header << ", " << lhist_index_label(i * step + min) << ")"; } std::string bar(bar_width, '@'); res << std::setw(16) << std::left << header.str() << std::setw(8) << std::right << values.at(i) << " |" << std::setw(max_width) << std::left << bar << "|" << std::endl; } return res.str(); } void TextOutput::map_hist( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const { out_ << map_hist_to_str( bpftrace, map, top, div, values_by_key, total_counts_by_key); out_ << std::endl; } void TextOutput::map_stats( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, int64_t>> &total_counts_by_key) const { out_ << map_stats_to_str( bpftrace, map, top, div, values_by_key, total_counts_by_key); out_ << std::endl << std::endl; } void TextOutput::value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const { out_ << value_to_str(bpftrace, ty, value, false, 1) << std::endl; } void TextOutput::message(MessageType type __attribute__((unused)), const std::string& msg, bool nl) const { out_ << msg; if (nl) out_ << std::endl; } void TextOutput::lost_events(uint64_t lost) const { out_ << "Lost " << lost << " events" << std::endl; } void TextOutput::attached_probes(uint64_t num_probes) const { if (num_probes == 1) out_ << "Attaching " << num_probes << " probe..." << std::endl; else out_ << "Attaching " << num_probes << " probes..." << std::endl; } std::string TextOutput::field_to_str(const std::string &name, const std::string &value) const { return "." + name + " = " + value; } std::string TextOutput::tuple_to_str( const std::vector &elems) const { return "(" + str_join(elems, ", ") + ")"; } std::string TextOutput::map_key_to_str(BPFtrace &bpftrace, IMap &map, const std::vector &key) const { return map.name_ + map.key_.argument_value_list_str(bpftrace, key); } std::string TextOutput::map_keyval_to_str(IMap &map, const std::string &key, const std::string &val) const { std::string res = key + ": "; if (map.type_.IsHistTy() || map.type_.IsLhistTy()) res += "\n"; res += val; return res; } std::string TextOutput::map_elem_delim_to_str(IMap &map) const { if (map.type_.type != Type::kstack && map.type_.type != Type::ustack && map.type_.type != Type::ksym && map.type_.type != Type::usym && map.type_.type != Type::inet) return "\n"; return ""; } std::string TextOutput::key_value_pairs_to_str( std::vector> &keyvals) const { std::vector elems; for (auto &e : keyvals) elems.push_back(e.first + " " + std::to_string(e.second)); return str_join(elems, ", "); } std::string JsonOutput::json_escape(const std::string &str) const { std::ostringstream escaped; for (const char &c : str) { switch (c) { case '"': escaped << "\\\""; break; case '\\': escaped << "\\\\"; break; case '\n': escaped << "\\n"; break; case '\r': escaped << "\\r"; break; case '\t': escaped << "\\t"; break; default: if ('\x00' <= c && c <= '\x1f') { escaped << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int)c; } else { escaped << c; } } } return escaped.str(); } void JsonOutput::map( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { if (values_by_key.empty()) return; out_ << "{\"type\": \"" << MessageType::map << "\", \"data\": {"; out_ << "\"" << json_escape(map.name_) << "\": "; if (map.key_.size() > 0) // check if this map has keys out_ << "{"; out_ << map_to_str(bpftrace, map, top, div, values_by_key); if (map.key_.size() > 0) out_ << "}"; out_ << "}}" << std::endl; } std::string JsonOutput::hist_to_str(const std::vector &values, uint32_t div) const { int min_index, max_index, max_value; hist_prepare(values, min_index, max_index, max_value); if (max_index == -1) return ""; std::ostringstream res; res << "["; for (int i = min_index; i <= max_index; i++) { if (i > min_index) res << ", "; res << "{"; if (i == 0) { res << "\"max\": -1, "; } else if (i == 1) { res << "\"min\": 0, \"max\": 0, "; } else if (i == 2) { res << "\"min\": 1, \"max\": 1, "; } else { long low = 1 << (i-2); long high = (1 << (i-2+1)) - 1; res << "\"min\": " << low << ", \"max\": " << high << ", "; } res << "\"count\": " << values.at(i) / div; res << "}"; } res << "]"; return res.str(); } std::string JsonOutput::lhist_to_str(const std::vector &values, int min, int max, int step) const { int max_index, max_value, buckets, start_value, end_value; lhist_prepare(values, min, max, step, max_index, max_value, buckets, start_value, end_value); if (max_index == -1) return ""; std::ostringstream res; res << "["; for (int i = start_value; i <= end_value; i++) { if (i > start_value) res << ", "; res << "{"; if (i == 0) { res << "\"max\": " << min - 1 << ", "; } else if (i == (buckets + 1)) { res << "\"min\": " << max << ", "; } else { long low = (i - 1) * step + min; long high = i * step + min - 1; res << "\"min\": " << low << ", \"max\": " << high << ", "; } res << "\"count\": " << values.at(i); res << "}"; } res << "]"; return res.str(); } void JsonOutput::map_hist( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const { if (total_counts_by_key.empty()) return; out_ << "{\"type\": \"" << MessageType::hist << "\", \"data\": {"; out_ << "\"" << json_escape(map.name_) << "\": "; if (map.key_.size() > 0) // check if this map has keys out_ << "{"; out_ << map_hist_to_str( bpftrace, map, top, div, values_by_key, total_counts_by_key); if (map.key_.size() > 0) out_ << "}"; out_ << "}}" << std::endl; } void JsonOutput::map_stats( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, int64_t>> &total_counts_by_key) const { if (total_counts_by_key.empty()) return; out_ << "{\"type\": \"" << MessageType::stats << "\", \"data\": {"; out_ << "\"" << json_escape(map.name_) << "\": "; if (map.key_.size() > 0) // check if this map has keys out_ << "{"; out_ << map_stats_to_str( bpftrace, map, top, div, values_by_key, total_counts_by_key); if (map.key_.size() > 0) out_ << "}"; out_ << "}}" << std::endl; } void JsonOutput::value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const { out_ << "{\"type\": \"" << MessageType::value << "\", \"data\": " << value_to_str(bpftrace, ty, value, false, 1) << "}" << std::endl; } void JsonOutput::message(MessageType type, const std::string& msg, bool nl __attribute__((unused))) const { out_ << "{\"type\": \"" << type << "\", \"data\": \"" << json_escape(msg) << "\"}" << std::endl; } void JsonOutput::message(MessageType type, const std::string& field, uint64_t value) const { out_ << "{\"type\": \"" << type << "\", \"data\": " << "{\"" << field << "\": " << value << "}" << "}" << std::endl; } void JsonOutput::lost_events(uint64_t lost) const { message(MessageType::lost_events, "events", lost); } void JsonOutput::attached_probes(uint64_t num_probes) const { message(MessageType::attached_probes, "probes", num_probes); } std::string JsonOutput::field_to_str(const std::string &name, const std::string &value) const { return "\"" + name + "\": " + value; } std::string JsonOutput::tuple_to_str( const std::vector &elems) const { return "[" + str_join(elems, ",") + "]"; } std::string JsonOutput::value_to_str(BPFtrace &bpftrace, const SizedType &type, std::vector &value, bool is_per_cpu, uint32_t div) const { auto str = Output::value_to_str(bpftrace, type, value, is_per_cpu, div); if (is_quoted_type(type)) return "\"" + json_escape(str) + "\""; else return str; } std::string JsonOutput::map_key_to_str(BPFtrace &bpftrace, IMap &map, const std::vector &key) const { std::vector args = map.key_.argument_value_list(bpftrace, key); if (!args.empty()) { return "\"" + json_escape(str_join(args, ",")) + "\""; } return ""; } std::string JsonOutput::map_keyval_to_str(IMap &map __attribute__((unused)), const std::string &key, const std::string &val) const { return key.empty() ? val : key + ": " + val; } std::string JsonOutput::map_elem_delim_to_str(IMap &map __attribute__((unused))) const { return ", "; } std::string JsonOutput::key_value_pairs_to_str( std::vector> &keyvals) const { std::vector elems; for (auto &e : keyvals) elems.push_back("\"" + e.first + "\": " + std::to_string(e.second)); return "{" + str_join(elems, ", ") + "}"; } } // namespace bpftrace bpftrace-0.14.0/src/output.h000066400000000000000000000277031413460502400156720ustar00rootroot00000000000000#pragma once #include #include #include #include #include "imap.h" namespace bpftrace { enum class MessageType { // don't forget to update std::ostream& operator<<(std::ostream& out, // MessageType type) in output.cpp map, value, hist, stats, printf, time, cat, join, syscall, attached_probes, lost_events }; std::ostream& operator<<(std::ostream& out, MessageType type); // Abstract class (interface) for output // Provides default implementation of some methods for formatting map and // non-map values into strings. // All output formatters should extend this class and override (at least) pure // virtual methods. class Output { public: explicit Output(std::ostream& out = std::cout, std::ostream& err = std::cerr) : out_(out),err_(err) { } Output(const Output &) = delete; Output& operator=(const Output &) = delete; virtual ~Output() = default; virtual std::ostream& outputstream() const { return out_; }; // Write map to output // Ideally, the implementation should use map_to_str to convert a map into // a string, format it properly, and print it to out_. virtual void map(BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const = 0; // Write map histogram to output // Ideally, the implementation should use map_hist_to_str to convert a map // histogram into a string, format it properly, and print it to out_. virtual void map_hist(BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const = 0; // Write map statistics to output // Ideally, the implementation should use map_stats_to_str to convert map // statistics into a string, format it properly, and print it to out_. virtual void map_stats( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, int64_t>> &total_counts_by_key) const = 0; // Write non-map value to output // Ideally, the implementation should use value_to_str to convert a value into // a string, format it properly, and print it to out_. virtual void value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const = 0; virtual void message(MessageType type, const std::string& msg, bool nl = true) const = 0; virtual void lost_events(uint64_t lost) const = 0; virtual void attached_probes(uint64_t num_probes) const = 0; protected: std::ostream &out_; std::ostream &err_; void hist_prepare(const std::vector &values, int &min_index, int &max_index, int &max_value) const; void lhist_prepare(const std::vector &values, int min, int max, int step, int &max_index, int &max_value, int &buckets, int &start_value, int &end_value) const; // Convert a log2 histogram into string virtual std::string hist_to_str(const std::vector &values, uint32_t div) const = 0; // Convert a linear histogram into string virtual std::string lhist_to_str(const std::vector &values, int min, int max, int step) const = 0; // Convert map into string // Default behaviour: format each (key, value) pair using output-specific // methods and join them into a single string virtual std::string map_to_str( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const; // Convert map histogram into string // Default behaviour: format each (key, hist) pair using output-specific // methods and join them into a single string virtual std::string map_hist_to_str( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const; // Convert map statistics into string // Default behaviour: format each (key, stats) pair using output-specific // methods and join them into a single string virtual std::string map_stats_to_str( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, int64_t>> &total_counts_by_key) const; // Convert map key to string virtual std::string map_key_to_str(BPFtrace &bpftrace, IMap &map, const std::vector &key) const = 0; // Properly join map key and value strings virtual std::string map_keyval_to_str(IMap &map, const std::string &key, const std::string &val) const = 0; // Delimiter to join (properly formatted) map elements into a single string virtual std::string map_elem_delim_to_str(IMap &map) const = 0; // Convert non-map value into string // This method should properly handle all non-map value types. // Aggregate types (array, struct, tuple) are formatted using output-specific // methods. virtual std::string value_to_str(BPFtrace &bpftrace, const SizedType &type, std::vector &value, bool is_per_cpu = false, uint32_t div = 1) const; // Convert an array to string // Default behaviour: [elem1, elem2, ...] virtual std::string array_to_str(const std::vector &elems) const; // Convert a struct to string // Default behaviour: { elem1, elem2, ... } // elems are expected to be properly formatted virtual std::string struct_to_str( const std::vector &elems) const; // Convert struct field (given by its name and value) into string virtual std::string field_to_str(const std::string &name, const std::string &value) const = 0; // Convert tuple to string virtual std::string tuple_to_str( const std::vector &elems) const = 0; // Convert a vector of (key, value) pairs into string // keys are strings // values are 64-bit integers // Used for properly formatting map statistics virtual std::string key_value_pairs_to_str( std::vector> &keyvals) const = 0; }; class TextOutput : public Output { public: explicit TextOutput(std::ostream& out = std::cout, std::ostream& err = std::cerr) : Output(out, err) { } void map(BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const override; void map_hist(BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const override; void map_stats( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, int64_t>> &total_counts_by_key) const override; virtual void value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const override; void message(MessageType type, const std::string& msg, bool nl = true) const override; void lost_events(uint64_t lost) const override; void attached_probes(uint64_t num_probes) const override; protected: static std::string hist_index_label(int power); static std::string lhist_index_label(int number); virtual std::string hist_to_str(const std::vector &values, uint32_t div) const override; virtual std::string lhist_to_str(const std::vector &values, int min, int max, int step) const override; std::string map_key_to_str(BPFtrace &bpftrace, IMap &map, const std::vector &key) const override; std::string map_keyval_to_str(IMap &map, const std::string &key, const std::string &val) const override; std::string map_elem_delim_to_str(IMap &map) const override; std::string field_to_str(const std::string &name, const std::string &value) const override; std::string tuple_to_str( const std::vector &elems) const override; std::string key_value_pairs_to_str( std::vector> &keyvals) const override; }; class JsonOutput : public Output { public: explicit JsonOutput(std::ostream& out = std::cout, std::ostream& err = std::cerr) : Output(out, err) { } void map(BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const override; void map_hist(BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const override; void map_stats( BPFtrace &bpftrace, IMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, int64_t>> &total_counts_by_key) const override; virtual void value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const override; void message(MessageType type, const std::string& msg, bool nl = true) const override; void message(MessageType type, const std::string& field, uint64_t value) const; void lost_events(uint64_t lost) const override; void attached_probes(uint64_t num_probes) const override; private: std::string json_escape(const std::string &str) const; protected: std::string value_to_str(BPFtrace &bpftrace, const SizedType &type, std::vector &value, bool is_per_cpu, uint32_t div) const override; std::string hist_to_str(const std::vector &values, uint32_t div) const override; std::string lhist_to_str(const std::vector &values, int min, int max, int step) const override; std::string map_key_to_str(BPFtrace &bpftrace, IMap &map, const std::vector &key) const override; std::string map_keyval_to_str(IMap &map, const std::string &key, const std::string &val) const override; std::string map_elem_delim_to_str(IMap &map) const override; std::string field_to_str(const std::string &name, const std::string &value) const override; std::string tuple_to_str( const std::vector &elems) const override; std::string key_value_pairs_to_str( std::vector> &keyvals) const override; }; } // namespace bpftrace bpftrace-0.14.0/src/parser.yy000066400000000000000000000455311413460502400160370ustar00rootroot00000000000000%skeleton "lalr1.cc" %require "3.0.4" %defines %define api.namespace { bpftrace } %define parser_class_name { Parser } %define api.token.constructor %define api.value.type variant %define parse.assert %define parse.trace %expect 5 %define parse.error verbose %param { bpftrace::Driver &driver } %param { void *yyscanner } %locations // Forward declarations of classes referenced in the parser %code requires { #include #include namespace bpftrace { class Driver; namespace ast { class Node; } // namespace ast } // namespace bpftrace #include "ast/ast.h" } %{ #include #include "driver.h" #include "lexer.h" void yyerror(bpftrace::Driver &driver, const char *s); %} %token END 0 "end of file" COLON ":" SEMI ";" LBRACE "{" RBRACE "}" LBRACKET "[" RBRACKET "]" LPAREN "(" RPAREN ")" QUES "?" ENDPRED "end predicate" COMMA "," PARAMCOUNT "$#" ASSIGN "=" EQ "==" NE "!=" LE "<=" GE ">=" LEFT "<<" RIGHT ">>" LT "<" GT ">" LAND "&&" LOR "||" PLUS "+" INCREMENT "++" LEFTASSIGN "<<=" RIGHTASSIGN ">>=" PLUSASSIGN "+=" MINUSASSIGN "-=" MULASSIGN "*=" DIVASSIGN "/=" MODASSIGN "%=" BANDASSIGN "&=" BORASSIGN "|=" BXORASSIGN "^=" MINUS "-" DECREMENT "--" MUL "*" DIV "/" MOD "%" BAND "&" BOR "|" BXOR "^" LNOT "!" BNOT "~" DOT "." PTR "->" IF "if" ELSE "else" UNROLL "unroll" STRUCT "struct" UNION "union" WHILE "while" FOR "for" RETURN "return" CONTINUE "continue" BREAK "break" ; %token BUILTIN "builtin" %token CALL "call" %token CALL_BUILTIN "call_builtin" %token IDENT "identifier" %token PATH "path" %token CPREPROC "preprocessor directive" %token STRUCT_DEFN "struct definition" %token ENUM "enum" %token STRING "string" %token MAP "map" %token VAR "variable" %token PARAM "positional parameter" %token INT "integer" %token STACK_MODE "stack_mode" %type unary_op compound_op %type attach_point_def c_definitions ident %type attach_point %type attach_points %type call %type and_expr arith_expr primary_expr cast_expr conditional_expr equality_expr expr logical_and_expr %type logical_or_expr map_or_var or_expr postfix_expr relational_expr shift_expr tuple_access_expr unary_expr xor_expr %type vargs %type int %type map %type param %type pred %type probe %type probes %type assign_stmt block_stmt expr_stmt if_stmt jump_stmt loop_stmt %type block block_or_if stmt_list %type var %left COMMA %right ASSIGN LEFTASSIGN RIGHTASSIGN PLUSASSIGN MINUSASSIGN MULASSIGN DIVASSIGN MODASSIGN BANDASSIGN BORASSIGN BXORASSIGN %left QUES COLON %left LOR %left LAND %left BOR %left BXOR %left BAND %left EQ NE %left LE GE LT GT %left LEFT RIGHT %left PLUS MINUS %left MUL DIV MOD %right LNOT BNOT %left LPAREN RPAREN LBRACKET RBRACKET DOT PTR %start program %% program: c_definitions probes END { driver.root_ = new ast::Program($1, $2); } ; c_definitions: CPREPROC c_definitions { $$ = $1 + "\n" + $2; } | STRUCT_DEFN c_definitions { $$ = $1 + ";\n" + $2; } | ENUM c_definitions { $$ = $1 + ";\n" + $2; } | %empty { $$ = std::string(); } ; probes: probes probe { $$ = $1; $1->push_back($2); } | probe { $$ = new ast::ProbeList; $$->push_back($1); } ; probe: attach_points pred block { if (!driver.listing_) $$ = new ast::Probe($1, $2, $3); else { error(@$, "unexpected listing query format"); YYERROR; } } | attach_points END { if (driver.listing_) $$ = new ast::Probe($1, nullptr, nullptr); else { error(@$, "unexpected end of file, expected {"); YYERROR; } } ; attach_points: attach_points "," attach_point { $$ = $1; $1->push_back($3); } | attach_point { $$ = new ast::AttachPointList; $$->push_back($1); } ; attach_point: attach_point_def { $$ = new ast::AttachPoint($1, @$); } ; attach_point_def: attach_point_def ident { $$ = $1 + $2; } // Since we're double quoting the STRING for the benefit of the // AttachPointParser, we have to make sure we re-escape any double // quotes. | attach_point_def STRING { $$ = $1 + "\"" + std::regex_replace($2, std::regex("\""), "\\\"") + "\""; } | attach_point_def PATH { $$ = $1 + $2; } | attach_point_def INT { $$ = $1 + std::to_string($2); } | attach_point_def COLON { $$ = $1 + ":"; } | attach_point_def DOT { $$ = $1 + "."; } | attach_point_def PLUS { $$ = $1 + "+"; } | attach_point_def MUL { $$ = $1 + "*"; } | attach_point_def LBRACKET { $$ = $1 + "["; } | attach_point_def RBRACKET { $$ = $1 + "]"; } | attach_point_def param { if ($2->ptype != PositionalParameterType::positional) { error(@$, "Not a positional parameter"); YYERROR; } // "Un-parse" the positional parameter back into text so // we can give it to the AttachPointParser. This is kind of // a hack but there doesn't look to be any other way. $$ = $1 + "$" + std::to_string($2->n); delete $2; } | %empty { $$ = ""; } ; pred: DIV expr ENDPRED { $$ = new ast::Predicate($2, @$); } | %empty { $$ = nullptr; } ; param: PARAM { try { long n = std::stol($1.substr(1, $1.size()-1)); if (n == 0) throw std::exception(); $$ = new ast::PositionalParameter(PositionalParameterType::positional, n, @$); } catch (std::exception const& e) { error(@1, "param " + $1 + " is out of integer range [1, " + std::to_string(std::numeric_limits::max()) + "]"); YYERROR; } } | PARAMCOUNT { $$ = new ast::PositionalParameter(PositionalParameterType::count, 0, @$); } ; block: "{" stmt_list "}" { $$ = $2; } ; stmt_list: /* * expressions must be followed by a semicolon _unless_ its the last one */ expr_stmt ";" stmt_list { $$ = $3; $3->insert($3->begin(), $1); } | expr_stmt { $$ = new ast::StatementList; $$->push_back($1); } /* * blocks can't be followed by semicolon */ | block_stmt stmt_list { $$ = $2; $2->insert($2->begin(), $1); } | %empty { $$ = new ast::StatementList; } ; block_stmt: loop_stmt { $$ = $1; } | if_stmt { $$ = $1; } ; expr_stmt: expr { $$ = new ast::ExprStatement($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 { $$ = new ast::Jump(ast::JumpType::BREAK, @$); } | CONTINUE { $$ = new ast::Jump(ast::JumpType::CONTINUE, @$); } | RETURN { $$ = new ast::Jump(ast::JumpType::RETURN, @$); } ; loop_stmt: UNROLL "(" int ")" block { $$ = new ast::Unroll($3, $5, @1 + @4); } | UNROLL "(" param ")" block { $$ = new ast::Unroll($3, $5, @1 + @4); } | WHILE "(" expr ")" block { $$ = new ast::While($3, $5, @1); } ; if_stmt: IF "(" expr ")" block { $$ = new ast::If($3, $5); } | IF "(" expr ")" block ELSE block_or_if { $$ = new ast::If($3, $5, $7); } ; block_or_if: block { $$ = $1; } | if_stmt { $$ = new ast::StatementList; $$->emplace_back($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 { $$ = new ast::AssignMapStatement($1, $3, false, @2); } | var ASSIGN expr { $$ = new ast::AssignVarStatement($1, $3, false, @2); } | map compound_op unary_expr { auto b = new ast::Binop($1, $2, $3, @2); $$ = new ast::AssignMapStatement($1, b, true, @$); } | var compound_op unary_expr { auto b = new ast::Binop($1, $2, $3, @2); $$ = new ast::AssignVarStatement($1, b, true, @$); } ; primary_expr: IDENT { $$ = new ast::Identifier($1, @$); } | int { $$ = $1; } | STRING { $$ = new ast::String($1, @$); } | STACK_MODE { $$ = new ast::StackMode($1, @$); } | BUILTIN { $$ = new ast::Builtin($1, @$); } | CALL_BUILTIN { $$ = new ast::Builtin($1, @$); } | LPAREN expr RPAREN { $$ = $2; } | param { $$ = $1; } | map_or_var { $$ = $1; } | "("expr "," vargs ")" { auto args = new ast::ExpressionList; args->emplace_back($2); args->insert(args->end(), $4->begin(), $4->end()); $$ = new ast::Tuple(args, @$); } ; postfix_expr: primary_expr { $$ = $1; } /* pointer */ | postfix_expr DOT ident { $$ = new ast::FieldAccess($1, $3, @2); } | postfix_expr PTR ident { $$ = new ast::FieldAccess(new ast::Unop(ast::Operator::MUL, $1, @2), $3, @$); } /* tuple */ | tuple_access_expr { $$ = $1; } /* array */ | postfix_expr "[" expr "]" { $$ = new ast::ArrayAccess($1, $3, @2 + @4); } | call { $$ = $1; } | map_or_var INCREMENT { $$ = new ast::Unop(ast::Operator::INCREMENT, $1, true, @2); } | map_or_var DECREMENT { $$ = new ast::Unop(ast::Operator::DECREMENT, $1, true, @2); } /* errors */ | INCREMENT ident { error(@1, "The ++ operator must be applied to a map or variable"); YYERROR; } | DECREMENT ident { error(@1, "The -- operator must be applied to a map or variable"); YYERROR; } ; /* Tuple factored out so we can use it in the tuple field assignment error */ tuple_access_expr: postfix_expr DOT INT { $$ = new ast::FieldAccess($1, $3, @3); } ; unary_expr: unary_op cast_expr { $$ = new ast::Unop($1, $2, @1); } | postfix_expr { $$ = $1; } | INCREMENT map_or_var { $$ = new ast::Unop(ast::Operator::INCREMENT, $2, @1); } | DECREMENT map_or_var { $$ = new ast::Unop(ast::Operator::DECREMENT, $2, @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 { $$ = new ast::Ternary($1, $3, $5, @$); } ; logical_or_expr: logical_and_expr { $$ = $1; } | logical_or_expr LOR logical_and_expr { $$ = new ast::Binop($1, ast::Operator::LOR, $3, @2); } ; logical_and_expr: or_expr { $$ = $1; } | logical_and_expr LAND or_expr { $$ = new ast::Binop($1, ast::Operator::LAND, $3, @2); } ; or_expr: xor_expr { $$ = $1; } | or_expr BOR xor_expr { $$ = new ast::Binop($1, ast::Operator::BOR, $3, @2); } ; xor_expr: and_expr { $$ = $1; } | xor_expr BXOR and_expr { $$ = new ast::Binop($1, ast::Operator::BXOR, $3, @2); } ; and_expr: equality_expr { $$ = $1; } | and_expr BAND equality_expr { $$ = new ast::Binop($1, ast::Operator::BAND, $3, @2); } ; equality_expr: relational_expr { $$ = $1; } | equality_expr EQ relational_expr { $$ = new ast::Binop($1, ast::Operator::EQ, $3, @2); } | equality_expr NE relational_expr { $$ = new ast::Binop($1, ast::Operator::NE, $3, @2); } ; relational_expr: shift_expr { $$ = $1; } | relational_expr LE shift_expr { $$ = new ast::Binop($1, ast::Operator::LE, $3, @2); } | relational_expr GE shift_expr { $$ = new ast::Binop($1, ast::Operator::GE, $3, @2); } | relational_expr LT shift_expr { $$ = new ast::Binop($1, ast::Operator::LT, $3, @2); } | relational_expr GT shift_expr { $$ = new ast::Binop($1, ast::Operator::GT, $3, @2); } ; shift_expr: arith_expr { $$ = $1; } | shift_expr LEFT arith_expr { $$ = new ast::Binop($1, ast::Operator::LEFT, $3, @2); } | shift_expr RIGHT arith_expr { $$ = new ast::Binop($1, ast::Operator::RIGHT, $3, @2); } ; arith_expr: cast_expr { $$ = $1; } | arith_expr PLUS cast_expr { $$ = new ast::Binop($1, ast::Operator::PLUS, $3, @2); } | arith_expr MINUS cast_expr { $$ = new ast::Binop($1, ast::Operator::MINUS, $3, @2); } | arith_expr MUL cast_expr { $$ = new ast::Binop($1, ast::Operator::MUL, $3, @2); } | arith_expr DIV cast_expr { $$ = new ast::Binop($1, ast::Operator::DIV, $3, @2); } | arith_expr MOD cast_expr { $$ = new ast::Binop($1, ast::Operator::MOD, $3, @2); } ; cast_expr: unary_expr { $$ = $1; } | LPAREN IDENT RPAREN cast_expr { $$ = new ast::Cast($2, false, false, $4, @1 + @3); } | LPAREN IDENT MUL RPAREN cast_expr { $$ = new ast::Cast($2, true, false, $5, @1 + @3); } | LPAREN IDENT MUL MUL RPAREN cast_expr { $$ = new ast::Cast($2, true, true, $6, @1 + @3); } ; int: MINUS INT { $$ = new ast::Integer((long)(~(unsigned long)($2) + 1), @$); } | INT { $$ = new ast::Integer($1, @$); } ; ident: IDENT { $$ = $1; } | BUILTIN { $$ = $1; } | CALL { $$ = $1; } | CALL_BUILTIN { $$ = $1; } | STACK_MODE { $$ = $1; } ; call: CALL "(" ")" { $$ = new ast::Call($1, @$); } | CALL "(" vargs ")" { $$ = new ast::Call($1, $3, @$); } | CALL_BUILTIN "(" ")" { $$ = new ast::Call($1, @$); } | CALL_BUILTIN "(" vargs ")" { $$ = new ast::Call($1, $3, @$); } | IDENT "(" ")" { error(@1, "Unknown function: " + $1); YYERROR; } | IDENT "(" vargs ")" { error(@1, "Unknown function: " + $1); YYERROR; } | BUILTIN "(" ")" { error(@1, "Unknown function: " + $1); YYERROR; } | BUILTIN "(" vargs ")" { error(@1, "Unknown function: " + $1); YYERROR; } | STACK_MODE "(" ")" { error(@1, "Unknown function: " + $1); YYERROR; } | STACK_MODE "(" vargs ")" { error(@1, "Unknown function: " + $1); YYERROR; } ; map: MAP { $$ = new ast::Map($1, @$); } | MAP "[" vargs "]" { $$ = new ast::Map($1, $3, @$); } ; var: VAR { $$ = new ast::Variable($1, @$); } ; map_or_var: var { $$ = $1; } | map { $$ = $1; } ; vargs: vargs "," expr { $$ = $1; $1->push_back($3); } | expr { $$ = new ast::ExpressionList; $$->push_back($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.14.0/src/printf.cpp000066400000000000000000000056041413460502400161630ustar00rootroot00000000000000#include #include "printf.h" #include "printf_format_types.h" #include "struct.h" namespace bpftrace { std::string verify_format_string(const std::string &fmt, std::vector args) { std::stringstream message; auto tokens_begin = std::sregex_iterator(fmt.begin(), fmt.end(), format_specifier_re); auto tokens_end = std::sregex_iterator(); auto num_tokens = std::distance(tokens_begin, tokens_end); int num_args = args.size(); if (num_args < num_tokens) { message << "printf: Not enough arguments for format string (" << num_args << " supplied, " << num_tokens << " expected)" << std::endl; return message.str(); } if (num_args > num_tokens) { message << "printf: Too many arguments for format string (" << num_args << " supplied, " << num_tokens << " expected)" << std::endl; return message.str(); } auto token_iter = tokens_begin; for (int i=0; istr()[offset] == '-') offset++; while ((token_iter->str()[offset] >= '0' && token_iter->str()[offset] <= '9') || token_iter->str()[offset] == '.') offset++; const std::string token = token_iter->str().substr(offset); const auto token_type_iter = printf_format_types.find(token); if (token_type_iter == printf_format_types.end()) { message << "printf: Unknown format string token: %" << token << std::endl; return message.str(); } const Type &token_type = token_type_iter->second; if (arg_type != token_type) { message << "printf: %" << token << " specifier expects a value of type " << token_type << " (" << arg_type << " supplied)" << std::endl; return message.str(); } } return ""; } int PrintableString::print(char *buf, size_t size, const char *fmt) { return snprintf(buf, size, fmt, value_.c_str()); } int PrintableCString::print(char *buf, size_t size, const char *fmt) { return snprintf(buf, size, fmt, value_); } int PrintableInt::print(char *buf, size_t size, const char *fmt) { return snprintf(buf, size, fmt, value_); } int PrintableSInt::print(char *buf, size_t size, const char *fmt) { return snprintf(buf, size, fmt, value_); } } // namespace bpftrace bpftrace-0.14.0/src/printf.h000066400000000000000000000030651413460502400156270ustar00rootroot00000000000000#pragma once #include #include "ast/ast.h" #include "printf_format_types.h" #include "types.h" namespace bpftrace { static const std::string generate_pattern_string() { std::string pattern = "%-?[0-9]*(\\.[0-9]+)?("; for (const auto& e : printf_format_types) { pattern += e.first + "|"; } pattern.pop_back(); // for the last "|" pattern += ")"; return pattern; } const std::regex format_specifier_re(generate_pattern_string()); struct Field; std::string verify_format_string(const std::string& fmt, std::vector args); class IPrintable { public: virtual ~IPrintable() { }; virtual int print(char* buf, size_t size, const char* fmt) = 0; }; class PrintableString : public virtual IPrintable { public: PrintableString(std::string value) : value_(std::move(value)) { } int print(char* buf, size_t size, const char* fmt) override; private: std::string value_; }; class PrintableCString : public virtual IPrintable { public: PrintableCString(char* value) : value_(value) { } int print(char* buf, size_t size, const char* fmt) override; private: char* value_; }; class PrintableInt : public virtual IPrintable { public: PrintableInt(uint64_t value) : value_(value) { } int print(char* buf, size_t size, const char* fmt) override; private: uint64_t value_; }; class PrintableSInt : public virtual IPrintable { public: PrintableSInt(int64_t value) : value_(value) { } int print(char* buf, size_t size, const char* fmt) override; private: int64_t value_; }; } // namespace bpftrace bpftrace-0.14.0/src/printf_format_types.h000066400000000000000000000034121413460502400204170ustar00rootroot00000000000000#pragma once #include #include "types.h" namespace bpftrace { // clang-format off // valid printf length + specifier combinations // it's done like this because as of this writing, C++ doesn't have a builtin way to do // compile time string concatenation. here is the Python 3 code that generates this map: // #!/usr/bin/python3 // lengths = ("", "hh", "h", "l", "ll", "j", "z", "t") // specifiers = ("d", "u", "x", "X", "p") // // print("{\"s\", Type::string},") // print("{\"r\", Type::buffer},") // print(",\n".join([f"{{\"{l+s}\", Type::integer}}" for l in lengths for s in specifiers])) const std::unordered_map printf_format_types = { {"s", Type::string}, {"r", Type::buffer}, {"c", Type::integer}, {"d", Type::integer}, {"u", Type::integer}, {"x", Type::integer}, {"X", Type::integer}, {"p", Type::integer}, {"hhd", Type::integer}, {"hhu", Type::integer}, {"hhx", Type::integer}, {"hhX", Type::integer}, {"hhp", Type::integer}, {"hd", Type::integer}, {"hu", Type::integer}, {"hx", Type::integer}, {"hX", Type::integer}, {"hp", Type::integer}, {"ld", Type::integer}, {"lu", Type::integer}, {"lx", Type::integer}, {"lX", Type::integer}, {"lp", Type::integer}, {"lld", Type::integer}, {"llu", Type::integer}, {"llx", Type::integer}, {"llX", Type::integer}, {"llp", Type::integer}, {"jd", Type::integer}, {"ju", Type::integer}, {"jx", Type::integer}, {"jX", Type::integer}, {"jp", Type::integer}, {"zd", Type::integer}, {"zu", Type::integer}, {"zx", Type::integer}, {"zX", Type::integer}, {"zp", Type::integer}, {"td", Type::integer}, {"tu", Type::integer}, {"tx", Type::integer}, {"tX", Type::integer}, {"tp", Type::integer} }; // clang-format on } // namespace bpftrace bpftrace-0.14.0/src/probe_matcher.cpp000066400000000000000000000364441413460502400175010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "bpftrace.h" #include "cxxdemangler/cxxdemangler.h" #include "dwarf_parser.h" #include "log.h" #include "probe_matcher.h" #include "utils.h" #include #ifdef HAVE_BCC_ELF_FOREACH_SYM #include #include #endif namespace bpftrace { #ifdef HAVE_BCC_ELF_FOREACH_SYM static int add_symbol(const char* symname, uint64_t /*start*/, uint64_t /*size*/, void* payload) { auto syms = static_cast*>(payload); syms->insert(std::string(symname)); return 0; } #endif /* * Splits input string by '*' delimiter and return the individual parts. * Sets start_wildcard and end_wildcard if input starts or ends with '*'. */ std::vector get_tokens(const std::string& input, bool& start_wildcard, bool& end_wildcard) { if (input.empty()) return {}; start_wildcard = input[0] == '*'; end_wildcard = input[input.length() - 1] == '*'; std::vector tokens = split_string(input, '*'); tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end()); return tokens; } /* * Finds all matches of search_input in the provided input stream. * * If `ignore_trailing_module` is true, will ignore trailing kernel module. * For example, `[ehci_hcd]` will be ignored in: * ehci_disable_ASE [ehci_hcd] */ std::set ProbeMatcher::get_matches_in_stream( const std::string& search_input, bool ignore_trailing_module, std::istream& symbol_stream, const char delim) { bool start_wildcard, end_wildcard; auto tokens = get_tokens(search_input, start_wildcard, end_wildcard); std::string line; std::set matches; while (std::getline(symbol_stream, line, delim)) { if (ignore_trailing_module && symbol_has_module(line)) { line = strip_symbol_module(line); } if (!wildcard_match(line, tokens, start_wildcard, end_wildcard)) { auto fun_line = line; auto prefix = fun_line.find(':') != std::string::npos ? erase_prefix(fun_line) + ":" : ""; if (symbol_has_cpp_mangled_signature(fun_line)) { char* demangled_name = cxxdemangle(fun_line.c_str()); if (demangled_name) { if (!wildcard_match(prefix + demangled_name, tokens, true, true)) { free(demangled_name); } else { free(demangled_name); 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; } std::unique_ptr ProbeMatcher::get_iter_symbols(void) const { return std::make_unique("task\ntask_file"); } /* * 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) { std::unique_ptr symbol_stream; bool ignore_trailing_module = false; switch (probe_type) { case ProbeType::kprobe: case ProbeType::kretprobe: { symbol_stream = get_symbols_from_file(kprobe_path); ignore_trailing_module = true; break; } case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: { symbol_stream = get_func_symbols_from_file(target); break; } case ProbeType::tracepoint: { symbol_stream = get_symbols_from_file(tp_avail_path); 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::kfunc: case ProbeType::kretfunc: { symbol_stream = bpftrace_->btf_.get_all_funcs(); break; } case ProbeType::iter: { symbol_stream = get_iter_symbols(); break; } default: return {}; } if (symbol_stream) return get_matches_in_stream(search_input, ignore_trailing_module, *symbol_stream); else return {}; } /* * Find all matches of search_input in set */ std::set ProbeMatcher::get_matches_in_set( const std::string& search_input, const std::set& set) { std::string stream_in; // Strings in the set may contain a newline character, so we use '$' // as a delimiter. for (auto& str : set) stream_in.append(str + "$"); std::istringstream stream(stream_in); return get_matches_in_stream(search_input, false, stream, '$'); } std::unique_ptr ProbeMatcher::get_symbols_from_file( const std::string& path) const { auto file = std::make_unique(path); if (file->fail()) { throw std::runtime_error("Could not read symbols from " + path + ": " + strerror(errno)); } return file; } std::unique_ptr ProbeMatcher::get_func_symbols_from_file( const std::string& path) const { if (path.empty()) return std::make_unique(""); std::vector real_paths; if (path.find('*') != std::string::npos) real_paths = resolve_binary_path(path); else real_paths.push_back(path); #ifdef HAVE_BCC_ELF_FOREACH_SYM 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); #endif std::string result; for (auto& real_path : real_paths) { std::set syms; #ifdef HAVE_BCC_ELF_FOREACH_SYM // 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; } #else std::string call_str = std::string("objdump -tT ") + real_path + +" | " + "grep \"F .text\" | grep -oE '[^[:space:]]+$'"; const char* call = call_str.c_str(); std::istringstream iss(exec_system(call)); std::copy(std::istream_iterator(iss), std::istream_iterator(), std::inserter(syms, syms.begin())); #endif for (auto& sym : syms) result += real_path + ":" + sym + "\n"; } return std::make_unique(result); } std::unique_ptr ProbeMatcher::get_symbols_from_usdt( int pid, const std::string& target) const { std::string probes; usdt_probe_list usdt_probes; if (pid > 0) usdt_probes = USDTHelper::probes_for_pid(pid); else if (!target.empty()) { std::vector real_paths; if (target.find('*') != std::string::npos) real_paths = resolve_binary_path(target); else real_paths.push_back(target); for (auto& real_path : real_paths) { auto target_usdt_probes = USDTHelper::probes_for_path(real_path); usdt_probes.insert(usdt_probes.end(), target_usdt_probes.begin(), target_usdt_probes.end()); } } for (auto const& usdt_probe : usdt_probes) { std::string path = usdt_probe.path; std::string provider = usdt_probe.provider; std::string fname = usdt_probe.name; probes += path + ":" + provider + ":" + fname + "\n"; } return std::make_unique(probes); } std::unique_ptr ProbeMatcher::get_symbols_from_list( const std::vector& probes_list) const { std::string symbols; for (auto& probe : probes_list) symbols += probe.path + ":\n"; return std::make_unique(symbols); } /* * Get list of kernel probe types for the purpose of listing. * Ignore return probes. */ std::unique_ptr ProbeMatcher::kernel_probe_list() { std::string probes; for (auto& p : PROBE_LIST) { if (p.type == ProbeType::kfunc) { // kfunc must be available if (bpftrace_->btf_.has_data()) probes += p.name + "\n"; } else if (p.name.find("ret") == std::string::npos && !is_userspace_probe(p.type) && p.type != ProbeType::interval && p.type != ProbeType::profile && p.type != ProbeType::watchpoint && p.type != ProbeType::asyncwatchpoint) { probes += p.name + "\n"; } } return std::make_unique(probes); } /* * Get list of userspace probe types for the purpose of listing. * Ignore return probes. */ std::unique_ptr ProbeMatcher::userspace_probe_list() { std::string probes; for (auto& p : PROBE_LIST) { if (p.name.find("ret") == std::string::npos && is_userspace_probe(p.type) && p.name != "BEGIN" && p.name != "END") probes += p.name + "\n"; } return std::make_unique(probes); } FuncParamLists ProbeMatcher::get_tracepoints_params( const std::set& tracepoints) { FuncParamLists params; for (auto& tracepoint : tracepoints) { auto event = tracepoint; auto category = erase_prefix(event); std::string format_file_path = tp_path + "/" + category + "/" + event + "/format"; std::ifstream format_file(format_file_path.c_str()); std::string line; if (format_file.fail()) { LOG(ERROR) << "tracepoint format file not found: " << format_file_path; return {}; } // Skip lines until the first empty line do { getline(format_file, line); } while (line.length() > 0); while (getline(format_file, line)) { if (line.find("\tfield:") == 0) { size_t col_pos = line.find(':') + 1; params[tracepoint].push_back( line.substr(col_pos, line.find(';') - col_pos)); } } } return params; } FuncParamLists ProbeMatcher::get_iters_params( const std::set& iters) { FuncParamLists params; for (auto& iter : iters) { if (iter == "task") { params[iter].push_back("struct task_struct * task"); } else if (iter == "task_file") { params[iter].push_back("struct task_struct * task"); params[iter].push_back("int fd"); params[iter].push_back("struct file * file"); } } return params; } FuncParamLists ProbeMatcher::get_uprobe_params( const std::set& uprobes) { FuncParamLists params; static std::set warned_paths; for (auto& match : uprobes) { std::string fun = match; std::string path = erase_prefix(fun); auto dwarf = Dwarf::GetFromBinary(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::kfunc || probe_type == ProbeType::kretfunc) param_lists = bpftrace_->btf_.get_params(matches); else if (probe_type == ProbeType::iter) param_lists = get_iters_params(matches); else if (probe_type == ProbeType::uprobe) param_lists = get_uprobe_params(matches); } for (auto& match : matches) { std::cout << probetypeName(probe_type) << ":" << match << 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: case ProbeType::kfunc: case ProbeType::kretfunc: case ProbeType::iter: { search_input = attach_point.func; break; } case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::tracepoint: case ProbeType::hardware: case ProbeType::software: { search_input = attach_point.target + ":" + attach_point.func; break; } case ProbeType::usdt: { auto target = attach_point.target; // If PID is specified, targets in symbol_stream will have the // "/proc//root" prefix followed by an absolute path, so we make the // target absolute and add a leading wildcard. if (bpftrace_->pid() > 0) { if (!target.empty()) { if (auto abs_target = abs_path(target)) target = "*" + abs_target.value(); } else target = "*"; } auto ns = attach_point.ns.empty() ? "*" : attach_point.ns; search_input = target + ":" + ns + ":" + attach_point.func; break; } default: 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); } std::set ProbeMatcher::expand_probetype_kernel( const std::string& probe_type) { if (has_wildcard(probe_type)) return get_matches_in_stream(probe_type, false, *kernel_probe_list()); else return { probe_type }; } std::set ProbeMatcher::expand_probetype_userspace( const std::string& probe_type) { if (has_wildcard(probe_type)) return get_matches_in_stream(probe_type, false, *userspace_probe_list()); else return { probe_type }; } void ProbeMatcher::list_structs(const std::string& search) { auto structs = bpftrace_->btf_.get_all_structs(); std::string search_input = search; // If verbose is on, structs will contain full definitions if (bt_verbose) search_input += " *{*}*"; for (auto& match : get_matches_in_set(search_input, structs)) std::cout << match << std::endl; } } // namespace bpftrace bpftrace-0.14.0/src/probe_matcher.h000066400000000000000000000112271413460502400171360ustar00rootroot00000000000000#pragma once #include "ast/ast.h" #include #include #include namespace bpftrace { const std::string kprobe_path = "/sys/kernel/debug/tracing/available_filter_functions"; const std::string tp_avail_path = "/sys/kernel/debug/tracing/available_events"; const std::string tp_path = "/sys/kernel/debug/tracing/events"; struct ProbeListItem { std::string path; std::string alias; uint32_t type; uint64_t defaultp; }; // clang-format off const std::vector SW_PROBE_LIST = { { "alignment-faults", "", PERF_COUNT_SW_ALIGNMENT_FAULTS, 1 }, { "bpf-output", "", PERF_COUNT_SW_BPF_OUTPUT, 1 }, { "context-switches", "cs", PERF_COUNT_SW_CONTEXT_SWITCHES, 1000 }, { "cpu-clock", "cpu", PERF_COUNT_SW_CPU_CLOCK, 1000000 }, { "cpu-migrations", "", PERF_COUNT_SW_CPU_MIGRATIONS, 1 }, { "dummy", "", PERF_COUNT_SW_DUMMY, 1 }, { "emulation-faults", "", PERF_COUNT_SW_EMULATION_FAULTS, 1 }, { "major-faults", "", PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1 }, { "minor-faults", "", PERF_COUNT_SW_PAGE_FAULTS_MIN, 100 }, { "page-faults", "faults", PERF_COUNT_SW_PAGE_FAULTS, 100 }, { "task-clock", "", PERF_COUNT_SW_TASK_CLOCK, 1 }, }; const std::vector HW_PROBE_LIST = { { "backend-stalls", "", PERF_COUNT_HW_STALLED_CYCLES_BACKEND, 1000000 }, { "branch-instructions", "branches", PERF_COUNT_HW_BRANCH_INSTRUCTIONS, 100000 }, { "branch-misses", "", PERF_COUNT_HW_BRANCH_MISSES, 100000 }, { "bus-cycles", "", PERF_COUNT_HW_BUS_CYCLES, 100000 }, { "cache-misses", "", PERF_COUNT_HW_CACHE_MISSES, 1000000 }, { "cache-references", "", PERF_COUNT_HW_CACHE_REFERENCES, 1000000 }, { "cpu-cycles", "cycles", PERF_COUNT_HW_CPU_CYCLES, 1000000 }, { "frontend-stalls", "", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, 1000000 }, { "instructions", "", PERF_COUNT_HW_INSTRUCTIONS, 1000000 }, { "ref-cycles", "", PERF_COUNT_HW_REF_CPU_CYCLES, 1000000 } }; // clang-format on class BPFtrace; typedef std::map> FuncParamLists; class ProbeMatcher { public: explicit ProbeMatcher(BPFtrace *bpftrace) : bpftrace_(bpftrace) { } virtual ~ProbeMatcher() = default; /* * Get all matches for attach point containing a wildcard. * The output strings format depends on the probe type. */ std::set get_matches_for_ap( const ast::AttachPoint &attach_point); /* * Expanding probe type containing a wildcard. */ std::set expand_probetype_kernel(const std::string &probe_type); std::set expand_probetype_userspace( const std::string &probe_type); /* * Match all probes in prog and print them to stdout. */ void list_probes(ast::Program *prog); /* * Print definitions of structures matching search. */ void list_structs(const std::string &search); const BPFtrace *bpftrace_; private: std::set get_matches_in_stream(const std::string &search_input, bool ignore_trailing_module, std::istream &symbol_stream, const char delim = '\n'); std::set get_matches_for_probetype( const ProbeType &probe_type, const std::string &target, const std::string &search_input); std::set get_matches_in_set(const std::string &search_input, const std::set &set); virtual std::unique_ptr get_symbols_from_file( const std::string &path) const; virtual std::unique_ptr get_func_symbols_from_file( const std::string &path) const; virtual std::unique_ptr get_symbols_from_usdt( int pid, const std::string &target) const; virtual std::unique_ptr get_symbols_from_list( const std::vector &probes_list) const; std::unique_ptr get_iter_symbols(void) const; std::unique_ptr kernel_probe_list(); std::unique_ptr userspace_probe_list(); FuncParamLists get_tracepoints_params( const std::set &tracepoints); FuncParamLists get_iters_params(const std::set &iters); FuncParamLists get_uprobe_params(const std::set &uprobes); }; } // namespace bpftrace bpftrace-0.14.0/src/procmon.cpp000066400000000000000000000041761413460502400163410ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "procmon.h" #include "utils.h" namespace bpftrace { #ifndef __NR_pidfd_open #define __NR_pidfd_open 434 #endif static std::system_error SYS_ERROR(std::string msg) { return std::system_error(errno, std::generic_category(), msg); } static inline int pidfd_open(int pid, unsigned int flags) { return syscall(__NR_pidfd_open, pid, flags); } ProcMon::ProcMon(const std::string& pid) { setup(parse_pid(pid)); } 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(void) { // 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.14.0/src/procmon.h000066400000000000000000000016261413460502400160030ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace { class ProcMonBase { public: ProcMonBase() = default; virtual ~ProcMonBase() = default; /** Whether the process is still alive */ virtual bool is_alive(void) = 0; /** pid of the process being monitored */ pid_t pid(void) { return pid_; }; protected: int pid_ = -1; }; class ProcMon : public ProcMonBase { public: ProcMon(pid_t pid); ProcMon(const std::string& pid); ~ProcMon() override; // Disallow copying as the internal state will get out of sync which will // cause issues. ProcMon(const ProcMon&) = delete; ProcMon& operator=(const ProcMon&) = delete; ProcMon(ProcMon&&) = delete; ProcMon& operator=(ProcMon&&) = delete; bool is_alive(void) override; private: int pidfd_ = -1; char proc_path_[32]; bool died_ = false; void setup(pid_t pid); }; } // namespace bpftrace bpftrace-0.14.0/src/relocator.cpp000066400000000000000000000022721413460502400166510ustar00rootroot00000000000000#include "relocator.h" #include #include "bpftrace.h" #include "log.h" namespace bpftrace { Relocator::Relocator(std::tuple func, BPFtrace &bpftrace) : insns_(reinterpret_cast(std::get<0>(func))), nr_(std::get<1>(func) / sizeof(struct bpf_insn)), bpftrace_(bpftrace) { } int Relocator::relocate() { for (uintptr_t i = 0; i < nr_; ++i) { struct bpf_insn *insn = &insns_[i]; // Relocate mapid -> mapfd // // This relocation keeps codegen independent of runtime state (such as FD // numbers). This helps make codegen tests more reliable and enables // features such as AOT compilation. if (insn->code == BPF_DW && (insn->src_reg == BPF_PSEUDO_MAP_FD || insn->src_reg == BPF_PSEUDO_MAP_VALUE)) { auto mapid = insn->imm; auto map = bpftrace_.maps[mapid]; if (map) { insn->imm = static_cast((*map)->mapfd_); } else { LOG(ERROR) << "Failed to relocate mapid=" << mapid << ": ID unknown"; return 1; } ++i; // ldimm64 is 2 insns wide } } return 0; } } // namespace bpftrace bpftrace-0.14.0/src/relocator.h000066400000000000000000000012001413460502400163040ustar00rootroot00000000000000#pragma once #include #include struct bpf_insn; namespace bpftrace { class BPFtrace; // Relocates BPF bytecode prior to being loaded into the kernel // // Relocations are needed to customize the bytecode to the host the // bytecode is going to be run on. class Relocator { public: Relocator(std::tuple func, BPFtrace &bpftrace); ~Relocator() = default; // Perform relocations // // Note this function will modify the instructions passed into the // constructor int relocate(); private: struct bpf_insn *insns_; uintptr_t nr_; BPFtrace &bpftrace_; }; } // namespace bpftrace bpftrace-0.14.0/src/required_resources.cpp000066400000000000000000000115661413460502400205770ustar00rootroot00000000000000#include "required_resources.h" #include #include #include #include #include #include #include #include "bpftrace.h" #include "fake_map.h" #include "log.h" #include "utils.h" namespace bpftrace { int RequiredResources::create_maps(BPFtrace &bpftrace, bool fake) { if (fake) return create_maps_impl(bpftrace, fake); else return create_maps_impl(bpftrace, fake); } template int RequiredResources::create_maps_impl(BPFtrace &bpftrace, bool fake) { uint32_t failed_maps = 0; auto is_invalid_map = [=](int a) -> uint8_t { return (!fake && a < 0) ? 1 : 0; }; for (auto &map_val : map_vals) { std::string map_name = map_val.first; SizedType type = map_val.second; auto search_args = map_keys.find(map_name); if (search_args == map_keys.end()) LOG(FATAL) << "map key \"" << map_name << "\" not found"; auto &key = search_args->second; if (type.IsLhistTy()) { auto args = lhist_args.find(map_name); if (args == lhist_args.end()) LOG(FATAL) << "map arg \"" << map_name << "\" not found"; auto min = args->second.min; auto max = args->second.max; auto step = args->second.step; auto map = std::make_unique( map_name, type, key, min, max, step, bpftrace.mapmax_); failed_maps += is_invalid_map(map->mapfd_); bpftrace.maps.Add(std::move(map)); } else { auto map = std::make_unique(map_name, type, key, bpftrace.mapmax_); failed_maps += is_invalid_map(map->mapfd_); bpftrace.maps.Add(std::move(map)); } } for (StackType stack_type : stackid_maps) { // The stack type doesn't matter here, so we use kstack to force SizedType // to set stack_size. auto map = std::make_unique(CreateStack(true, stack_type)); failed_maps += is_invalid_map(map->mapfd_); bpftrace.maps.Set(stack_type, std::move(map)); } if (needs_join_map) { // join uses map storage as we'd like to process data larger than can fit on // the BPF stack. int value_size = 8 + 8 + bpftrace.join_argnum_ * bpftrace.join_argsize_; auto map = std::make_unique( "join", BPF_MAP_TYPE_PERCPU_ARRAY, 4, value_size, 1, 0); failed_maps += is_invalid_map(map->mapfd_); bpftrace.maps.Set(MapManager::Type::Join, std::move(map)); } if (needs_elapsed_map) { std::string map_ident = "elapsed"; SizedType type = CreateUInt64(); MapKey key; auto map = std::make_unique(map_ident, type, key, 1); failed_maps += is_invalid_map(map->mapfd_); bpftrace.maps.Set(MapManager::Type::Elapsed, std::move(map)); } if (needs_data_map) { // get size of all the format strings size_t size = 0; for (auto it : seq_printf_args) size += std::get<0>(it).size() + 1; // compute buffer size to hold all the formats and create map with that int ptr_size = sizeof(unsigned long); size = (size / ptr_size + 1) * ptr_size; auto map = std::make_unique("data", BPF_MAP_TYPE_ARRAY, 4, size, 1, 0); // copy all the format strings to buffer, head to tail uint32_t idx = 0; std::vector formats(size, 0); for (auto &arg : seq_printf_args) { auto str = std::get<0>(arg).c_str(); auto len = std::get<0>(arg).size(); memcpy(&formats.data()[idx], str, len); idx += len + 1; } // store the data in map uint64_t id = 0; int ret = bpf_update_elem(map->mapfd_, &id, formats.data(), 0); if (is_invalid_map(map->mapfd_) || (!fake && ret == -1)) failed_maps += 1; bpftrace.maps.Set(MapManager::Type::SeqPrintfData, std::move(map)); } { auto map = std::make_unique(BPF_MAP_TYPE_PERF_EVENT_ARRAY); failed_maps += is_invalid_map(map->mapfd_); bpftrace.maps.Set(MapManager::Type::PerfEvent, std::move(map)); } if (failed_maps > 0) { std::cerr << "Creation of the required BPF maps has failed." << std::endl; std::cerr << "Make sure you have all the required permissions and are not"; std::cerr << " confined (e.g. like" << std::endl; std::cerr << "snapcraft does). `dmesg` will likely have useful output for"; std::cerr << " further troubleshooting" << std::endl; } return failed_maps; } void RequiredResources::save_state(std::ostream &out) const { cereal::BinaryOutputArchive archive(out); archive(*this); } void RequiredResources::load_state(std::istream &in) { cereal::BinaryInputArchive archive(in); archive(*this); } void RequiredResources::load_state(const uint8_t *ptr, size_t len) { auto addr = const_cast(ptr); Membuf mbuf(addr, addr + len); std::istream istream(&mbuf); cereal::BinaryInputArchive archive(istream); archive(*this); } } // namespace bpftrace bpftrace-0.14.0/src/required_resources.h000066400000000000000000000104751413460502400202420ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include "location.hh" #include "mapkey.h" #include "mapmanager.h" #include "struct.h" #include "types.h" namespace bpftrace { class BPFtrace; struct HelperErrorInfo { int func_id = -1; location loc; }; struct LinearHistogramArgs { long min = -1; long max = -1; long step = -1; private: friend class cereal::access; template void serialize(Archive &archive) { archive(min, max, step); } }; // 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: // Create maps in `maps` based on stored metadata // // If `fake` is set, then `FakeMap`s will be created. This is useful for: // * allocating map IDs for codegen, because there's no need to prematurely // create resources that may not get used (debug mode, AOT codepath, etc.) // * unit tests, as unit tests should not make system state changes // // Returns 0 on success, number of maps that failed to be created otherwise int create_maps(BPFtrace &bpftrace, bool fake); // `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>> system_args; std::vector>> seq_printf_args; std::vector> seq_printf_ids; std::vector join_args; std::vector time_args; std::vector strftime_args; std::vector>> cat_args; std::vector non_map_print_args; // Async argument metadata that codegen creates. Ideally ResourceAnalyser // pass should be collecting this, but it's complex to move the logic. // // Don't add more async arguments here!. std::unordered_map helper_error_info; // `printf_args` is created here but the field offsets are fixed up // by codegen -- only codegen knows data layout to compute offsets std::vector>> printf_args; std::vector probe_ids; // Map metadata std::map map_vals; std::map lhist_args; std::map map_keys; std::unordered_set stackid_maps; bool needs_join_map = false; bool needs_elapsed_map = false; bool needs_data_map = false; // Probe metadata // // Probe metadata that codegen creates. Ideally ResourceAnalyser pass should // be collecting this, but it's complex to move the logic. std::vector probes; std::vector special_probes; std::vector watchpoint_probes; private: template int create_maps_impl(BPFtrace &bpftrace, bool fake); friend class cereal::access; template void serialize(Archive &archive) { archive(system_args, seq_printf_args, seq_printf_ids, join_args, time_args, strftime_args, cat_args, non_map_print_args, // Hard to annotate flex types, so skip // helper_error_info, printf_args, probe_ids, map_vals, lhist_args, map_keys, stackid_maps, needs_join_map, needs_elapsed_map, needs_data_map, probes, special_probes); } }; } // namespace bpftrace bpftrace-0.14.0/src/resolve_cgroupid.cpp000066400000000000000000000043011413460502400202250ustar00rootroot00000000000000#ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #ifdef HAVE_NAME_TO_HANDLE_AT # include # include #else # include # include #endif #include #include #include #include #include "act_helpers.h" #include "resolve_cgroupid.h" namespace { #ifndef HAVE_NAME_TO_HANDLE_AT struct file_handle { unsigned int handle_bytes; int handle_type; char f_handle[0]; }; int name_to_handle_at(int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags) { return (int)syscall(SYS_name_to_handle_at, dirfd, pathname, handle, mount_id, flags); } #endif // Not embedding file_handle directly in cgid_file_handle, because C++ // has problems with zero-sized array as struct members and // file_handle's f_handle is a zero-sized array. // // Also, not embedding file_handle through the public inheritance, // since checking for member offsets with offsetof within a // non-standard-layout type is conditionally-supported (so compilers // are not required to support it). And we want to do it to make sure // we got the wrapper right. // // Hence open coding the file_handle members directly in // cgid_file_handle and the static asserts following it. struct cgid_file_handle { file_handle *as_file_handle_ptr() { return reinterpret_cast(this); } unsigned int handle_bytes = sizeof(std::uint64_t); int handle_type; std::uint64_t cgid; }; ACTH_ASSERT_SAME_SIZE(cgid_file_handle, file_handle, std::uint64_t); ACTH_ASSERT_SAME_MEMBER(cgid_file_handle, handle_bytes, file_handle, handle_bytes); ACTH_ASSERT_SAME_MEMBER(cgid_file_handle, handle_type, file_handle, handle_type); ACTH_ASSERT_SAME_OFFSET(cgid_file_handle, cgid, file_handle, f_handle); } namespace bpftrace_linux { std::uint64_t resolve_cgroupid(const std::string &path) { cgid_file_handle cfh; int mount_id; int err = name_to_handle_at(AT_FDCWD, path.c_str(), cfh.as_file_handle_ptr(), &mount_id, 0); if (err < 0) { auto emsg = std::strerror(errno); throw std::runtime_error("Failed to get `cgroupid` for path: \"" + path + "\": " + emsg); } return cfh.cgid; } } bpftrace-0.14.0/src/resolve_cgroupid.h000066400000000000000000000002141413460502400176710ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace_linux { std::uint64_t resolve_cgroupid(const std::string &path); } bpftrace-0.14.0/src/struct.cpp000066400000000000000000000075641413460502400162140ustar00rootroot00000000000000#include "struct.h" #include namespace bpftrace { bool Bitfield::operator==(const Bitfield &other) const { return read_bytes == other.read_bytes && mask == other.mask && access_rshift == other.access_rshift; } bool Bitfield::operator!=(const Bitfield &other) const { return !(*this == other); } std::unique_ptr Struct::CreateTuple(std::vector fields) { // See llvm::StructLayout::StructLayout source std::unique_ptr tuple(new Struct(0)); ssize_t offset = 0; ssize_t struct_align = 1; for (auto &field : fields) { auto align = field.GetAlignment(); struct_align = std::max(align, struct_align); auto size = field.GetSize(); auto padding = (align - (offset % align)) % align; if (padding) tuple->padded = true; offset += padding; tuple->fields.push_back(Field{ .name = "", .type = field, .offset = offset, .is_bitfield = false, .bitfield = Bitfield{}, }); offset += size; } auto padding = (struct_align - (offset % struct_align)) % struct_align; tuple->size = offset + padding; tuple->align = struct_align; return tuple; } void Struct::Dump(std::ostream &os) { os << " {" << std::endl; auto pad = [](int size) -> std::string { return "__pad[" + std::to_string(size) + "]"; }; auto prefix = [](int offset, std::ostream &os) -> std::ostream & { os << " " << std::setfill(' ') << std::setw(3) << offset << " | "; return os; }; ssize_t offset = 0; for (const auto &field : fields) { auto delta = field.offset - offset; if (delta) { prefix(offset, os) << pad(delta) << std::endl; } prefix(offset + delta, os) << field.type << std::endl; offset = field.offset + field.type.GetSize(); } os << "} sizeof: [" << size << "]" << std::endl; } bool Struct::HasField(const std::string &name) const { for (auto &field : fields) { if (field.name == name) return true; } return false; } const Field &Struct::GetField(const std::string &name) const { for (auto &field : fields) { if (field.name == name) return field; } throw std::runtime_error("struct has no field named " + name); } void Struct::AddField(const std::string &field_name, const SizedType &type, ssize_t offset, bool is_bitfield, const Bitfield &bitfield, bool is_data_loc) { if (!HasField(field_name)) fields.emplace_back(Field{ .name = field_name, .type = type, .offset = offset, .is_bitfield = is_bitfield, .bitfield = bitfield, .is_data_loc = is_data_loc }); } void StructManager::Add(const std::string &name, size_t size) { if (struct_map_.find(name) != struct_map_.end()) throw std::runtime_error("Type redefinition: type with name \'" + name + "\' already exists"); struct_map_[name] = std::make_unique(size); } std::weak_ptr StructManager::Lookup(const std::string &name) const { auto s = struct_map_.find(name); return s != struct_map_.end() ? s->second : std::weak_ptr(); } std::weak_ptr StructManager::LookupOrAdd(const std::string &name, size_t size) { auto s = struct_map_.insert({ name, std::make_unique(size) }); return s.first->second; } bool StructManager::Has(const std::string &name) const { return struct_map_.find(name) != struct_map_.end(); } std::weak_ptr StructManager::AddTuple(std::vector fields) { auto t = tuples_.insert(Struct::CreateTuple(std::move(fields))); return *t.first; } size_t StructManager::GetTuplesCnt() const { return tuples_.size(); } } // namespace bpftrace bpftrace-0.14.0/src/struct.h000066400000000000000000000075731413460502400156610ustar00rootroot00000000000000#pragma once #include #include #include #include "types.h" #include "utils.h" namespace bpftrace { struct Bitfield { bool operator==(const Bitfield &other) const; bool operator!=(const Bitfield &other) const; // Read `read_bytes` bytes starting from this field's offset size_t read_bytes; // Then rshift the resulting value by `access_rshift` to get field value size_t access_rshift; // Then logical AND `mask` to mask out everything but this bitfield uint64_t mask; private: friend class cereal::access; template void serialize(Archive &archive) { archive(read_bytes, access_rshift, mask); } }; struct Field { std::string name; SizedType type; ssize_t offset; bool is_bitfield; Bitfield bitfield; // Used for tracepoint __data_loc's // // If true, this field is a 32 bit integer whose value encodes information on // where to find the actual data. The first 2 bytes is the size of the data. // The last 2 bytes is the offset from the start of the tracepoint struct // where the data begins. bool is_data_loc = false; bool operator==(const Field &rhs) const { return name == rhs.name && type == rhs.type && offset == rhs.offset && is_bitfield == rhs.is_bitfield && bitfield == rhs.bitfield && is_data_loc == rhs.is_data_loc; } private: friend class cereal::access; template void serialize(Archive &archive) { archive(name, type, offset, is_bitfield, bitfield, is_data_loc); } }; using Fields = std::vector; struct Struct { int size = -1; // in bytes int align = 1; // in bytes, used for tuples only bool padded = false; Fields fields; Struct() = default; explicit Struct(int size) : size(size) { } bool HasField(const std::string &name) const; const Field &GetField(const std::string &name) const; void AddField(const std::string &field_name, const SizedType &type, ssize_t offset, bool is_bitfield, const Bitfield &bitfield, bool is_data_loc); static std::unique_ptr CreateTuple(std::vector fields); void Dump(std::ostream &os); bool operator==(const Struct &rhs) const { return size == rhs.size && align == rhs.align && padded == rhs.padded && fields == rhs.fields; } private: friend class cereal::access; template void serialize(Archive &archive) { archive(size, align, padded, fields); } }; std::ostream &operator<<(std::ostream &os, const Fields &t); } // namespace bpftrace namespace std { template <> struct hash { size_t operator()(const bpftrace::Struct &s) const { size_t hash = std::hash()(s.size); for (auto &field : s.fields) bpftrace::hash_combine(hash, field.type); return hash; } }; template <> struct hash> { size_t operator()(const std::shared_ptr &s_ptr) const { return std::hash()(*s_ptr); } }; template <> struct equal_to> { bool operator()(const std::shared_ptr &lhs, const std::shared_ptr &rhs) const { return *lhs == *rhs; } }; } // namespace std namespace bpftrace { class StructManager { public: // struct map manipulation void Add(const std::string &name, size_t size); std::weak_ptr Lookup(const std::string &name) const; std::weak_ptr LookupOrAdd(const std::string &name, size_t size); bool Has(const std::string &name) const; // tuples set manipulation std::weak_ptr AddTuple(std::vector fields); size_t GetTuplesCnt() const; private: std::map> struct_map_; std::unordered_set> tuples_; }; } // namespace bpftrace bpftrace-0.14.0/src/tracepoint_format_parser.cpp000066400000000000000000000210641413460502400217530ustar00rootroot00000000000000#include #include #include #include #include "ast/ast.h" #include "bpftrace.h" #include "log.h" #include "struct.h" #include "tracepoint_format_parser.h" namespace bpftrace { std::set TracepointFormatParser::struct_list; bool TracepointFormatParser::parse(ast::Program *program, BPFtrace &bpftrace) { std::vector probes_with_tracepoint; for (ast::Probe *probe : *program->probes) for (ast::AttachPoint *ap : *probe->attach_points) if (ap->provider == "tracepoint") { probes_with_tracepoint.push_back(probe); continue; } if (probes_with_tracepoint.empty()) return true; ast::TracepointArgsVisitor n{}; if (!bpftrace.btf_.has_data()) program->c_definitions += "#include \n"; for (ast::Probe *probe : probes_with_tracepoint) { n.visit(*probe); for (ast::AttachPoint *ap : *probe->attach_points) { if (ap->provider == "tracepoint") { std::string &category = ap->target; std::string &event_name = ap->func; std::string format_file_path = "/sys/kernel/debug/tracing/events/" + category + "/" + event_name + "/format"; glob_t glob_result; if (has_wildcard(category) || has_wildcard(event_name)) { // tracepoint wildcard expansion, part 1 of 3. struct definitions. memset(&glob_result, 0, sizeof(glob_result)); int ret = glob(format_file_path.c_str(), 0, NULL, &glob_result); if (ret != 0) { if (ret == GLOB_NOMATCH) { LOG(ERROR, ap->loc, std::cerr) << "tracepoints not found: " << category << ":" << event_name; // helper message: if (category == "syscall") LOG(ERROR, ap->loc, std::cerr) << "Did you mean syscalls:" << event_name << "?"; if (bt_verbose) { LOG(ERROR) << strerror(errno) << ": " << format_file_path; } return false; } else { // unexpected error LOG(ERROR, ap->loc, std::cerr) << std::string(strerror(errno)); return false; } } if (probe->tp_args_structs_level <= 0) { globfree(&glob_result); continue; } for (size_t i = 0; i < glob_result.gl_pathc; ++i) { std::string filename(glob_result.gl_pathv[i]); std::ifstream format_file(filename); std::string prefix("/sys/kernel/debug/tracing/events/"); size_t pos = prefix.length(); std::string real_category = filename.substr( pos, filename.find('/', pos) - pos); pos = prefix.length() + real_category.length() + 1; std::string real_event = filename.substr( pos, filename.length() - std::string("/format").length() - pos); // Check to avoid adding the same struct more than once to definitions std::string struct_name = get_struct_name(real_category, real_event); if (!TracepointFormatParser::struct_list.count(struct_name)) { program->c_definitions += get_tracepoint_struct( format_file, real_category, real_event, bpftrace); TracepointFormatParser::struct_list.insert(struct_name); } } globfree(&glob_result); } else { // single tracepoint std::ifstream format_file(format_file_path.c_str()); if (format_file.fail()) { // Errno might get clobbered by LOG(). int saved_errno = errno; LOG(ERROR, ap->loc, std::cerr) << "tracepoint not found: " << category << ":" << event_name; // helper message: if (category == "syscall") LOG(WARNING, ap->loc, std::cerr) << "Did you mean syscalls:" << event_name << "?"; if (bt_verbose) { // Having the location info isn't really useful here, so no // bpftrace.error LOG(ERROR) << strerror(saved_errno) << ": " << format_file_path; } return false; } if (probe->tp_args_structs_level <= 0) continue; // Check to avoid adding the same struct more than once to definitions std::string struct_name = get_struct_name(category, event_name); if (TracepointFormatParser::struct_list.insert(struct_name).second) program->c_definitions += get_tracepoint_struct( format_file, category, event_name, bpftrace); } } } } return true; } std::string TracepointFormatParser::get_struct_name(const std::string &category, const std::string &event_name) { return "struct _tracepoint_" + category + "_" + event_name; } std::string TracepointFormatParser::get_struct_name(const std::string &probe_id) { // probe_id has format category:event std::string event_name = probe_id; std::string category = erase_prefix(event_name); return get_struct_name(category, event_name); } std::string TracepointFormatParser::parse_field(const std::string &line, int *last_offset, BPFtrace &bpftrace) { std::string extra = ""; auto field_pos = line.find("field:"); if (field_pos == std::string::npos) return ""; auto field_semi_pos = line.find(';', field_pos); if (field_semi_pos == std::string::npos) return ""; auto offset_pos = line.find("offset:", field_semi_pos); if (offset_pos == std::string::npos) return ""; auto offset_semi_pos = line.find(';', offset_pos); if (offset_semi_pos == std::string::npos) return ""; auto size_pos = line.find("size:", offset_semi_pos); if (size_pos == std::string::npos) return ""; auto size_semi_pos = line.find(';', size_pos); if (size_semi_pos == std::string::npos) return ""; int size = std::stoi(line.substr(size_pos + 5, size_semi_pos - size_pos - 5)); int offset = std::stoi( line.substr(offset_pos + 7, offset_semi_pos - offset_pos - 7)); // If there'a gap between last field and this one, // generate padding fields if (offset && *last_offset) { int i, gap = offset - *last_offset; for (i = 0; i < gap; i++) { extra += " char __pad_" + std::to_string(offset - gap + i) + ";\n"; } } *last_offset = offset + size; std::string field = line.substr(field_pos + 6, field_semi_pos - field_pos - 6); auto field_type_end_pos = field.find_last_of("\t "); if (field_type_end_pos == std::string::npos) return ""; std::string field_type = field.substr(0, field_type_end_pos); std::string field_name = field.substr(field_type_end_pos+1); if (field_type.find("__data_loc") != std::string::npos) { // Note that the type here (ie `int`) does not matter. Later during parse // time the parser will rewrite this field type to a u64 so that it can // hold the pointer to the actual location of the data. field_type = R"_(__attribute__((annotate("tp_data_loc"))) int)_"; } // Only adjust field types for non-arrays if (field_name.find("[") == std::string::npos) field_type = adjust_integer_types(field_type, size); // If BTF is available, we try not to use any header files, including // and request all the types we need from BTF. bpftrace.btf_set_.emplace(field_type); return extra + " " + field_type + " " + field_name + ";\n"; } std::string TracepointFormatParser::adjust_integer_types(const std::string &field_type, int size) { std::string new_type = field_type; // Adjust integer fields to correctly sized types if (size == 8) { if (field_type == "int") new_type = "s64"; if (field_type == "unsigned int" || field_type == "unsigned" || field_type == "u32" || field_type == "pid_t" || field_type == "uid_t" || field_type == "gid_t") new_type = "u64"; } return new_type; } std::string TracepointFormatParser::get_tracepoint_struct( std::istream &format_file, const std::string &category, const std::string &event_name, BPFtrace &bpftrace) { std::string format_struct = get_struct_name(category, event_name) + "\n{\n"; int last_offset = 0; for (std::string line; getline(format_file, line); ) { format_struct += parse_field(line, &last_offset, bpftrace); } format_struct += "};\n"; return format_struct; } } // namespace bpftrace bpftrace-0.14.0/src/tracepoint_format_parser.h000066400000000000000000000032661413460502400214240ustar00rootroot00000000000000#pragma once #include #include #include "ast/visitors.h" #include "bpftrace.h" namespace bpftrace { namespace ast { class TracepointArgsVisitor : public Visitor { public: void visit(Builtin &builtin) override { Visitor::visit(builtin); if (builtin.ident == "args" && probe_->tp_args_structs_level == -1) probe_->tp_args_structs_level = 0; }; void visit(FieldAccess &acc) override { Visitor::visit(acc); if (probe_->tp_args_structs_level >= 0) probe_->tp_args_structs_level++; }; void visit(Probe &probe) override { probe_ = &probe; Visitor::visit(probe); }; private: Probe *probe_; }; } // namespace ast class TracepointFormatParser { public: static bool parse(ast::Program *program, BPFtrace &bpftrace); static std::string get_struct_name(const std::string &category, const std::string &event_name); static std::string get_struct_name(const std::string &probe_id); static void clear_struct_list() { struct_list.clear(); } private: static std::string parse_field(const std::string &line, int *last_offset, BPFtrace &bpftrace); static std::string adjust_integer_types(const std::string &field_type, int size); static std::set struct_list; protected: static std::string get_tracepoint_struct(std::istream &format_file, const std::string &category, const std::string &event_name, BPFtrace &bpftrace); }; } // namespace bpftrace bpftrace-0.14.0/src/triggers.h000066400000000000000000000002311413460502400161430ustar00rootroot00000000000000#pragma once extern "C" { void __attribute__((noinline)) BEGIN_trigger() { asm (""); } void __attribute__((noinline)) END_trigger() { asm (""); } } bpftrace-0.14.0/src/types.cpp000066400000000000000000000325521413460502400160270ustar00rootroot00000000000000#include #include #include #include "bpftrace.h" #include "log.h" #include "struct.h" #include "types.h" namespace bpftrace { std::ostream &operator<<(std::ostream &os, Type type) { os << typestr(type); return os; } std::ostream &operator<<(std::ostream &os, AddrSpace as) { os << addrspacestr(as); return os; } std::ostream &operator<<(std::ostream &os, ProbeType type) { os << probetypeName(type); return os; } std::ostream &operator<<(std::ostream &os, const SizedType &type) { if (type.IsRecordTy()) { os << type.GetName(); } else if (type.IsPtrTy()) { if (type.IsCtxAccess()) os << "(ctx) "; os << *type.GetPointeeTy() << " *"; } else if (type.IsIntTy()) { os << (type.is_signed_ ? "" : "unsigned ") << "int" << 8 * type.GetSize(); } else if (type.IsArrayTy()) { os << *type.GetElementTy() << "[" << type.GetNumElements() << "]"; } else if (type.IsStringTy() || type.IsBufferTy()) { os << type.type << "[" << type.GetSize() << "]"; } else if (type.IsTupleTy()) { os << "("; size_t n = type.GetFieldCount(); for (size_t i = 0; i < n; ++i) { os << type.GetField(i).type; if (i != n - 1) os << ","; } os << ")"; } else { os << type.type; } return os; } bool SizedType::IsSameType(const SizedType &t) const { if (t.type != type) return false; if (IsRecordTy()) return t.GetName() == GetName(); if (IsPtrTy() && t.IsPtrTy()) return GetPointeeTy()->IsSameType(*t.GetPointeeTy()); return type == t.type; } bool SizedType::IsEqual(const SizedType &t) const { if (t.type != type) return false; if (IsRecordTy()) return t.GetName() == GetName() && t.GetSize() == GetSize(); if (IsPtrTy()) return *t.GetPointeeTy() == *GetPointeeTy(); if (IsArrayTy()) return t.GetNumElements() == GetNumElements() && *t.GetElementTy() == *GetElementTy(); if (IsTupleTy()) return *t.GetStruct().lock() == *GetStruct().lock(); return type == t.type && GetSize() == t.GetSize() && is_signed_ == t.is_signed_; } bool SizedType::operator!=(const SizedType &t) const { return !IsEqual(t); } bool SizedType::operator==(const SizedType &t) const { return IsEqual(t); } bool SizedType::IsByteArray() const { return type == Type::string || type == Type::usym || type == Type::inet || type == Type::buffer || type == Type::timestamp || type == Type::mac_address; } bool SizedType::IsAggregate() const { return IsArrayTy() || IsByteArray() || IsTupleTy() || IsRecordTy(); } bool SizedType::IsStack() const { return type == Type::ustack || type == Type::kstack; } std::string addrspacestr(AddrSpace as) { switch (as) { case AddrSpace::kernel: return "kernel"; break; case AddrSpace::user: return "user"; break; case AddrSpace::none: return "none"; break; } return {}; // unreached } std::string typestr(Type t) { switch (t) { // clang-format off case Type::none: return "none"; break; case Type::integer: return "integer"; break; case Type::pointer: return "pointer"; break; case Type::record: return "record"; break; case Type::hist: return "hist"; break; case Type::lhist: return "lhist"; break; case Type::count: return "count"; break; case Type::sum: return "sum"; break; case Type::min: return "min"; break; case Type::max: return "max"; break; case Type::avg: return "avg"; break; case Type::stats: return "stats"; break; case Type::kstack: return "kstack"; break; case Type::ustack: return "ustack"; break; case Type::string: return "string"; break; case Type::ksym: return "ksym"; break; case Type::usym: return "usym"; break; case Type::probe: return "probe"; break; case Type::username: return "username"; break; case Type::inet: return "inet"; break; case Type::stack_mode:return "stack mode";break; case Type::array: return "array"; break; case Type::buffer: return "buffer"; break; case Type::tuple: return "tuple"; break; case Type::timestamp:return "timestamp";break; case Type::mac_address: return "mac_address"; break; // clang-format on } return {}; // unreached } ProbeType probetype(const std::string &probeName) { ProbeType retType = ProbeType::invalid; auto v = std::find_if(PROBE_LIST.begin(), PROBE_LIST.end(), [&probeName] (const ProbeItem& p) { return (p.name == probeName || p.abbr == probeName); }); if (v != PROBE_LIST.end()) retType = v->type; return retType; } std::string probetypeName(const std::string &probeName) { std::string res = probeName; auto v = std::find_if(PROBE_LIST.begin(), PROBE_LIST.end(), [&probeName] (const ProbeItem& p) { return (p.name == probeName || p.abbr == probeName); }); if (v != PROBE_LIST.end()) res = v->name; return res; } std::string probetypeName(ProbeType t) { switch (t) { case ProbeType::invalid: return "invalid"; 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::kfunc: return "kfunc"; break; case ProbeType::kretfunc: return "kretfunc"; break; case ProbeType::iter: return "iter"; break; } return {}; // unreached } bool is_userspace_probe(const ProbeType &probe_type) { return probe_type == ProbeType::uprobe || probe_type == ProbeType::uretprobe || probe_type == ProbeType::usdt; } uint64_t asyncactionint(AsyncAction a) { return (uint64_t)a; } // Type wrappers SizedType CreateInteger(size_t bits, bool is_signed) { // Zero sized integers are not usually valid. However, during semantic // analysis when we're inferring types, the first pass may not have // enough information to figure out the exact size of the integer. Later // passes infer the exact size. assert(bits == 0 || bits == 1 || bits == 8 || bits == 16 || bits == 32 || bits == 64); auto t = SizedType(Type::integer, bits / 8, is_signed); t.size_bits_ = bits; return t; } SizedType CreateBool(void) { return CreateInteger(1, false); } SizedType CreateInt(size_t bits) { return CreateInteger(bits, true); }; SizedType CreateUInt(size_t bits) { return CreateInteger(bits, false); } SizedType CreateInt8() { return CreateInt(8); } SizedType CreateInt16() { return CreateInt(16); } SizedType CreateInt32() { return CreateInt(32); } SizedType CreateInt64() { return CreateInt(64); } SizedType CreateUInt8() { return CreateUInt(8); } SizedType CreateUInt16() { return CreateUInt(16); } SizedType CreateUInt32() { return CreateUInt(32); } SizedType CreateUInt64() { return CreateUInt(64); } SizedType CreateString(size_t size) { return SizedType(Type::string, size); } SizedType CreateNone() { return SizedType(Type::none, 0); } SizedType CreateStackMode() { return SizedType(Type::stack_mode, 0); } SizedType CreateArray(size_t num_elements, const SizedType &element_type) { size_t size = num_elements * element_type.GetSize(); auto ty = SizedType(Type::array, size); ty.num_elements_ = num_elements; ty.element_type_ = std::make_shared(element_type); return ty; } SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as) { // Pointer itself is always an uint64 auto ty = SizedType(Type::pointer, 8); ty.element_type_ = std::make_shared(pointee_type); ty.SetAS(as); return ty; } SizedType CreateRecord(const std::string &name, std::weak_ptr record) { auto ty = SizedType(Type::record, record.expired() ? 0 : record.lock()->size); ty.name_ = name; ty.inner_struct_ = record; return ty; } SizedType CreateStack(bool kernel, StackType stack) { auto st = SizedType(kernel ? Type::kstack : Type::ustack, 8); st.stack_type = stack; return st; } SizedType CreateMin(bool is_signed) { return SizedType(Type::min, 8, is_signed); } SizedType CreateMax(bool is_signed) { return SizedType(Type::max, 8, is_signed); } SizedType CreateSum(bool is_signed) { return SizedType(Type::sum, 8, is_signed); } SizedType CreateCount(bool is_signed) { return SizedType(Type::count, 8, is_signed); } SizedType CreateAvg(bool is_signed) { return SizedType(Type::avg, 8, is_signed); } SizedType CreateStats(bool is_signed) { return SizedType(Type::stats, 8, is_signed); } SizedType CreateProbe() { return SizedType(Type::probe, 8); } SizedType CreateUsername() { return SizedType(Type::username, 8); } SizedType CreateInet(size_t size) { auto st = SizedType(Type::inet, size); st.is_internal = true; return st; } SizedType CreateLhist() { return SizedType(Type::lhist, 8); } SizedType CreateHist() { return SizedType(Type::hist, 8); } SizedType CreateUSym() { return SizedType(Type::usym, 16); } SizedType CreateKSym() { return SizedType(Type::ksym, 8); } SizedType CreateBuffer(size_t size) { return SizedType(Type::buffer, size); } SizedType CreateTimestamp() { return SizedType(Type::timestamp, 16); } SizedType CreateTuple(std::weak_ptr tuple) { auto s = SizedType(Type::tuple, tuple.lock()->size); s.inner_struct_ = tuple; return s; } SizedType CreateMacAddress() { auto st = SizedType(Type::mac_address, 6); st.is_internal = true; return st; } bool SizedType::IsSigned(void) const { return is_signed_; } std::vector &SizedType::GetFields() const { assert(IsTupleTy() || IsRecordTy()); return inner_struct_.lock()->fields; } Field &SizedType::GetField(ssize_t n) const { assert(IsTupleTy() || IsRecordTy()); if (n >= GetFieldCount()) throw std::runtime_error("Getfield(): out of bound"); return inner_struct_.lock()->fields[n]; } ssize_t SizedType::GetFieldCount() const { assert(IsTupleTy() || IsRecordTy()); return inner_struct_.lock()->fields.size(); } void SizedType::DumpStructure(std::ostream &os) { assert(IsTupleTy()); if (IsTupleTy()) os << "tuple"; else os << "struct"; return inner_struct_.lock()->Dump(os); } ssize_t SizedType::GetAlignment() const { if (IsStringTy()) return 1; if (IsTupleTy() || IsRecordTy()) return inner_struct_.lock()->align; if (GetSize() <= 2) return GetSize(); else if (IsArrayTy()) return element_type_->GetAlignment(); else if (IsByteArray() || GetSize() <= 4) return 4; else return 8; } bool SizedType::HasField(const std::string &name) const { assert(IsRecordTy()); return inner_struct_.lock()->HasField(name); } const Field &SizedType::GetField(const std::string &name) const { assert(IsRecordTy()); return inner_struct_.lock()->GetField(name); } std::weak_ptr SizedType::GetStruct() const { assert(IsRecordTy() || IsTupleTy()); return inner_struct_; } } // namespace bpftrace namespace std { size_t hash::operator()( const bpftrace::SizedType &type) const { auto hash = std::hash()(static_cast(type.type)); bpftrace::hash_combine(hash, type.GetSize()); switch (type.type) { case bpftrace::Type::integer: bpftrace::hash_combine(hash, type.IsSigned()); break; case bpftrace::Type::pointer: bpftrace::hash_combine(hash, *type.GetPointeeTy()); break; case bpftrace::Type::record: bpftrace::hash_combine(hash, type.GetName()); break; case bpftrace::Type::kstack: case bpftrace::Type::ustack: bpftrace::hash_combine(hash, type.stack_type); break; case bpftrace::Type::array: bpftrace::hash_combine(hash, *type.GetElementTy()); bpftrace::hash_combine(hash, type.GetNumElements()); break; case bpftrace::Type::tuple: bpftrace::hash_combine(hash, *type.GetStruct().lock()); break; // No default case (explicitly skip all remaining types instead) to get // a compiler warning when we add a new type case bpftrace::Type::none: case bpftrace::Type::hist: case bpftrace::Type::lhist: case bpftrace::Type::count: case bpftrace::Type::sum: case bpftrace::Type::min: case bpftrace::Type::max: case bpftrace::Type::avg: case bpftrace::Type::stats: case bpftrace::Type::string: case bpftrace::Type::ksym: case bpftrace::Type::usym: case bpftrace::Type::probe: case bpftrace::Type::username: case bpftrace::Type::inet: case bpftrace::Type::stack_mode: case bpftrace::Type::buffer: case bpftrace::Type::timestamp: case bpftrace::Type::mac_address: break; } return hash; } } // namespace std bpftrace-0.14.0/src/types.h000066400000000000000000000312121413460502400154640ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include namespace bpftrace { const int MAX_STACK_SIZE = 1024; const int DEFAULT_STACK_SIZE = 127; const int STRING_SIZE = 64; const int COMM_SIZE = 16; enum class Type { // clang-format off none, integer, pointer, record, // struct/union, as struct is a protected keyword hist, lhist, count, sum, min, max, avg, stats, kstack, ustack, string, ksym, usym, probe, username, inet, stack_mode, array, buffer, tuple, timestamp, mac_address // clang-format on }; enum class AddrSpace { none, kernel, user, }; std::ostream &operator<<(std::ostream &os, Type type); std::ostream &operator<<(std::ostream &os, AddrSpace as); enum class StackMode { bpftrace, perf, }; struct StackType { size_t limit = DEFAULT_STACK_SIZE; StackMode mode = StackMode::bpftrace; bool operator ==(const StackType &obj) const { return limit == obj.limit && mode == obj.mode; } private: friend class cereal::access; template void serialize(Archive &archive) { archive(limit, mode); } }; class BPFtrace; struct Struct; struct Field; class SizedType { public: SizedType() : type(Type::none), size_(0) { } SizedType(Type type, size_t size_, bool is_signed) : type(type), size_(size_), is_signed_(is_signed) { } SizedType(Type type, size_t size_) : type(type), size_(size_) { } Type type; StackType stack_type; bool is_internal = false; bool is_tparg = false; bool is_funcarg = false; int funcarg_idx = -1; private: size_t size_ = -1; // in bytes bool is_signed_ = false; std::shared_ptr element_type_; // for "container" and pointer // (like) types size_t num_elements_ = -1; // for array like types std::string name_; // name of this type, for named types like struct bool ctx_ = false; // Is bpf program context AddrSpace as_ = AddrSpace::none; ssize_t size_bits_ = -1; // size in bits for integer types std::weak_ptr inner_struct_; // inner struct for records and tuples // the actual Struct object is owned by // StructManager friend class cereal::access; template void serialize(Archive &archive) { archive(type, stack_type, is_internal, is_tparg, is_funcarg, funcarg_idx, size_, is_signed_, element_type_, num_elements_, name_, ctx_, as_, size_bits_, inner_struct_); } public: /** Tuple/struct accessors */ std::vector &GetFields() const; bool HasField(const std::string &name) const; const Field &GetField(const std::string &name) const; Field &GetField(ssize_t n) const; ssize_t GetFieldCount() const; std::weak_ptr GetStruct() const; /** Required alignment for this type */ ssize_t GetAlignment() const; /** Dump the underlying structure for debug purposes */ void DumpStructure(std::ostream &os); AddrSpace GetAS() const { return as_; } void SetAS(AddrSpace as) { as_ = as; } bool IsCtxAccess() const { return ctx_; }; void MarkCtxAccess() { ctx_ = true; }; bool IsByteArray() const; bool IsAggregate() const; bool IsStack() const; bool IsEqual(const SizedType &t) const; bool operator==(const SizedType &t) const; bool operator!=(const SizedType &t) const; bool IsSameType(const SizedType &t) const; bool IsPrintableTy() { return type != Type::none && type != Type::pointer && type != Type::stack_mode && !IsCtxAccess(); } bool IsSigned(void) const; size_t GetSize() const { return size_; } void SetSize(size_t size) { size_ = size; if (IsIntTy()) { assert(size == 0 || size == 1 || size == 8 || size == 16 || size == 32 || size == 64); size_bits_ = size * 8; } } size_t GetIntBitWidth() const { assert(IsIntTy()); return size_bits_; }; size_t GetNumElements() const { assert(IsArrayTy() || IsStringTy()); return IsStringTy() ? size_ : size_ / element_type_->size_; }; const std::string GetName() const { assert(IsRecordTy()); return name_; } const SizedType *GetElementTy() const { assert(IsArrayTy()); return element_type_.get(); } const SizedType *GetPointeeTy() const { assert(IsPtrTy()); return element_type_.get(); } bool IsBoolTy() const { return type == Type::integer && size_bits_ == 1; }; bool IsPtrTy() const { return type == Type::pointer; }; bool IsIntTy() const { return type == Type::integer; }; bool IsNoneTy(void) const { return type == Type::none; }; bool IsIntegerTy(void) const { return type == Type::integer; }; bool IsHistTy(void) const { return type == Type::hist; }; bool IsLhistTy(void) const { return type == Type::lhist; }; bool IsCountTy(void) const { return type == Type::count; }; bool IsSumTy(void) const { return type == Type::sum; }; bool IsMinTy(void) const { return type == Type::min; }; bool IsMaxTy(void) const { return type == Type::max; }; bool IsAvgTy(void) const { return type == Type::avg; }; bool IsStatsTy(void) const { return type == Type::stats; }; bool IsKstackTy(void) const { return type == Type::kstack; }; bool IsUstackTy(void) const { return type == Type::ustack; }; bool IsStringTy(void) const { return type == Type::string; }; bool IsKsymTy(void) const { return type == Type::ksym; }; bool IsUsymTy(void) const { return type == Type::usym; }; bool IsProbeTy(void) const { return type == Type::probe; }; bool IsUsernameTy(void) const { return type == Type::username; }; bool IsInetTy(void) const { return type == Type::inet; }; bool IsStackModeTy(void) const { return type == Type::stack_mode; }; bool IsArrayTy(void) const { return type == Type::array; }; bool IsRecordTy(void) const { return type == Type::record; }; bool IsBufferTy(void) const { return type == Type::buffer; }; bool IsTupleTy(void) const { return type == Type::tuple; }; bool IsTimestampTy(void) const { return type == Type::timestamp; }; bool IsMacAddressTy(void) const { return type == Type::mac_address; }; friend std::ostream &operator<<(std::ostream &, const SizedType &); friend std::ostream &operator<<(std::ostream &, Type); // Factories friend SizedType CreateArray(size_t num_elements, const SizedType &element_type); friend SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as); friend SizedType CreateRecord(const std::string &name, std::weak_ptr record); friend SizedType CreateInteger(size_t bits, bool is_signed); friend SizedType CreateTuple(std::weak_ptr tuple); }; // Type helpers SizedType CreateNone(); SizedType CreateBool(); SizedType CreateInteger(size_t bits, bool is_signed); SizedType CreateInt(size_t bits); SizedType CreateUInt(size_t bits); SizedType CreateInt8(); SizedType CreateInt16(); SizedType CreateInt32(); SizedType CreateInt64(); SizedType CreateUInt8(); SizedType CreateUInt16(); SizedType CreateUInt32(); SizedType CreateUInt64(); SizedType CreateString(size_t size); SizedType CreateArray(size_t num_elements, const SizedType &element_type); SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as = AddrSpace::none); SizedType CreateRecord(const std::string &name, std::weak_ptr record); SizedType CreateTuple(std::weak_ptr tuple); SizedType CreateStackMode(); SizedType CreateStack(bool kernel, StackType st = StackType()); SizedType CreateMin(bool is_signed); SizedType CreateMax(bool is_signed); SizedType CreateSum(bool is_signed); SizedType CreateCount(bool is_signed); SizedType CreateAvg(bool is_signed); SizedType CreateStats(bool is_signed); SizedType CreateProbe(); SizedType CreateUsername(); SizedType CreateInet(size_t size); SizedType CreateLhist(); SizedType CreateHist(); SizedType CreateUSym(); SizedType CreateKSym(); SizedType CreateBuffer(size_t size); SizedType CreateTimestamp(); SizedType CreateMacAddress(); std::ostream &operator<<(std::ostream &os, const SizedType &type); enum class ProbeType { invalid, kprobe, kretprobe, uprobe, uretprobe, usdt, tracepoint, profile, interval, software, hardware, watchpoint, asyncwatchpoint, kfunc, kretfunc, iter, }; std::ostream &operator<<(std::ostream &os, ProbeType type); struct ProbeItem { std::string name; std::string abbr; ProbeType type; }; const std::vector PROBE_LIST = { { "kprobe", "k", ProbeType::kprobe }, { "kretprobe", "kr", ProbeType::kretprobe }, { "uprobe", "u", ProbeType::uprobe }, { "uretprobe", "ur", ProbeType::uretprobe }, { "usdt", "U", ProbeType::usdt }, { "BEGIN", "BEGIN", ProbeType::uprobe }, { "END", "END", ProbeType::uprobe }, { "tracepoint", "t", ProbeType::tracepoint }, { "profile", "p", ProbeType::profile }, { "interval", "i", ProbeType::interval }, { "software", "s", ProbeType::software }, { "hardware", "h", ProbeType::hardware }, { "watchpoint", "w", ProbeType::watchpoint }, { "asyncwatchpoint", "aw", ProbeType::asyncwatchpoint }, { "kfunc", "f", ProbeType::kfunc }, { "kretfunc", "fr", ProbeType::kretfunc }, { "iter", "it", ProbeType::iter }, }; ProbeType probetype(const std::string &type); bool is_userspace_probe(const ProbeType &probe_type); std::string addrspacestr(AddrSpace as); std::string typestr(Type t); std::string probetypeName(const std::string &type); 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 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; pid_t pid = -1; 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; 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, pid, len, mode, async, address, func_offset); } }; typedef std::map ProbeArgs; const int RESERVED_IDS_PER_ASYNCACTION = 10000; enum class AsyncAction { // clang-format off printf = 0, // printf reserves 0-9999 for printf_ids syscall = 10000, // system reserves 10000-19999 for printf_ids cat = 20000, // cat reserves 20000-29999 for printf_ids exit = 30000, print, clear, zero, time, join, helper_error, print_non_map, strftime, watchpoint_attach, watchpoint_detach, // clang-format on }; uint64_t asyncactionint(AsyncAction a); enum class PositionalParameterType { positional, count }; } // namespace bpftrace // SizedType hash function // Allows to use SizedType in unordered_set/map. namespace std { template <> struct hash { size_t operator()(const bpftrace::StackType &obj) const { switch (obj.mode) { case bpftrace::StackMode::bpftrace: return std::hash()("bpftrace#" + to_string(obj.limit)); case bpftrace::StackMode::perf: return std::hash()("perf#" + to_string(obj.limit)); } return {}; // unreached } }; template <> struct hash { size_t operator()(const bpftrace::SizedType &type) const; }; } // namespace std bpftrace-0.14.0/src/usdt.cpp000066400000000000000000000104231413460502400156330ustar00rootroot00000000000000#include "usdt.h" #include "log.h" #include #include #include #include #include #include #include static std::unordered_set path_cache; static std::unordered_set pid_cache; // Maps all traced paths and all their providers to vector of tracepoints // on each provider static std::unordered_map> usdt_provider_cache; // Maps a pid to a set of paths for its probes static std::unordered_map> usdt_pid_to_paths_cache; // Used as a temporary buffer, during read_probes_for_pid to maintain // current tracepoint paths for the current pid static std::unordered_set current_pid_paths; static void usdt_probe_each(struct bcc_usdt *usdt_probe) { usdt_provider_cache[usdt_probe->bin_path][usdt_probe->provider].emplace_back( usdt_probe_entry{ .path = usdt_probe->bin_path, .provider = usdt_probe->provider, .name = usdt_probe->name, #ifdef LIBBCC_ATTACH_UPROBE_SEVEN_ARGS_SIGNATURE .semaphore_offset = usdt_probe->semaphore_offset, #else .semaphore_offset = 0, #endif .num_locations = usdt_probe->num_locations, }); current_pid_paths.emplace(usdt_probe->bin_path); } /** * Move the current pid paths onto the pid_to_paths_cache, and clear * current_pid_paths. */ static void cache_current_pid_paths(int pid) { usdt_pid_to_paths_cache[pid].merge(current_pid_paths); current_pid_paths.clear(); } std::optional USDTHelper::find(int pid, const std::string &target, const std::string &provider, const std::string &name) { usdt_probe_list probes; if (pid > 0) { read_probes_for_pid(pid); for (auto const &path : usdt_pid_to_paths_cache[pid]) { probes.insert(probes.end(), usdt_provider_cache[path][provider].begin(), usdt_provider_cache[path][provider].end()); } } else { read_probes_for_path(target); probes = usdt_provider_cache[target][provider]; } auto it = std::find_if(probes.begin(), probes.end(), [&name](const usdt_probe_entry &e) { return e.name == name; }); if (it != probes.end()) { return *it; } else { return std::nullopt; } } usdt_probe_list USDTHelper::probes_for_pid(int pid) { read_probes_for_pid(pid); usdt_probe_list probes; for (auto const &path : usdt_pid_to_paths_cache[pid]) { for (auto const &usdt_probes : usdt_provider_cache[path]) { probes.insert(probes.end(), usdt_probes.second.begin(), usdt_probes.second.end()); } } return probes; } usdt_probe_list USDTHelper::probes_for_path(const std::string &path) { read_probes_for_path(path); usdt_probe_list probes; for (auto const &usdt_probes : usdt_provider_cache[path]) { probes.insert(probes.end(), usdt_probes.second.begin(), usdt_probes.second.end()); } return probes; } void USDTHelper::read_probes_for_pid(int pid) { if (pid_cache.count(pid)) return; if (pid > 0) { void *ctx = bcc_usdt_new_frompid(pid, nullptr); if (ctx == nullptr) { LOG(ERROR) << "failed to initialize usdt context for pid: " << pid; if (kill(pid, 0) == -1 && errno == ESRCH) LOG(ERROR) << "hint: process not running"; return; } bcc_usdt_foreach(ctx, usdt_probe_each); bcc_usdt_close(ctx); cache_current_pid_paths(pid); pid_cache.emplace(pid); } else { LOG(ERROR) << "a pid must be specified to list USDT probes by PID"; } } void USDTHelper::read_probes_for_path(const std::string &path) { if (path_cache.count(path)) return; void *ctx = bcc_usdt_new_frompath(path.c_str()); if (ctx == nullptr) { LOG(ERROR) << "failed to initialize usdt context for path " << path; return; } bcc_usdt_foreach(ctx, usdt_probe_each); bcc_usdt_close(ctx); path_cache.emplace(path); } bpftrace-0.14.0/src/usdt.h000066400000000000000000000017031413460502400153010ustar00rootroot00000000000000#pragma once #include #include #include struct usdt_probe_entry { std::string path; std::string provider; std::string name; uint64_t semaphore_offset; int num_locations; }; typedef std::vector usdt_probe_list; // Note this class is fully static because bcc_usdt_foreach takes a function // pointer callback without a context variable. So we must keep global state. class USDTHelper { public: static std::optional find(int pid, const std::string &target, const std::string &provider, const std::string &name); static usdt_probe_list probes_for_pid(int pid); static usdt_probe_list probes_for_path(const std::string &path); private: static void read_probes_for_pid(int pid); static void read_probes_for_path(const std::string &path); }; bpftrace-0.14.0/src/utils.cpp000066400000000000000000000632511413460502400160230ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bpftrace.h" #include "log.h" #include "probe_matcher.h" #include "utils.h" #include #include #include #include #include #if __has_include() #include namespace std_filesystem = std::filesystem; #elif __has_include() #include namespace std_filesystem = std::experimental::filesystem; #else #error "neither nor are present" #endif namespace { std::vector read_cpu_range(std::string path) { std::ifstream cpus_range_stream { path }; std::vector cpus; std::string cpu_range; while (std::getline(cpus_range_stream, cpu_range, ',')) { std::size_t rangeop = cpu_range.find('-'); if (rangeop == std::string::npos) { cpus.push_back(std::stoi(cpu_range)); } else { int start = std::stoi(cpu_range.substr(0, rangeop)); int end = std::stoi(cpu_range.substr(rangeop + 1)); for (int i = start; i <= end; i++) cpus.push_back(i); } } return cpus; } std::vector expand_wildcard_path(const std::string& path) { glob_t glob_result; memset(&glob_result, 0, sizeof(glob_result)); if (glob(path.c_str(), GLOB_NOCHECK, nullptr, &glob_result)) { globfree(&glob_result); throw std::runtime_error("glob() failed"); } std::vector matching_paths; for (size_t i = 0; i < glob_result.gl_pathc; ++i) { matching_paths.push_back(std::string(glob_result.gl_pathv[i])); } globfree(&glob_result); return matching_paths; } std::vector expand_wildcard_paths(const std::vector& paths) { std::vector expanded_paths; for (const auto& p : paths) { auto ep = expand_wildcard_path(p); expanded_paths.insert(expanded_paths.end(), ep.begin(), ep.end()); } return expanded_paths; } } // namespace namespace bpftrace { //'borrowed' from libbpf's bpf_core_find_kernel_btf // from Andrii Nakryiko const struct vmlinux_location vmlinux_locs[] = { { "/sys/kernel/btf/vmlinux", true }, { "/boot/vmlinux-%1$s", false }, { "/lib/modules/%1$s/vmlinux-%1$s", false }, { "/lib/modules/%1$s/build/vmlinux", false }, { "/usr/lib/modules/%1$s/kernel/vmlinux", false }, { "/usr/lib/debug/boot/vmlinux-%1$s", false }, { "/usr/lib/debug/boot/vmlinux-%1$s.debug", false }, { "/usr/lib/debug/lib/modules/%1$s/vmlinux", false }, { nullptr, false }, }; static bool pid_in_different_mountns(int pid); static std::vector resolve_binary_path(const std::string &cmd, const char *env_paths, int pid); void StdioSilencer::silence() { fflush(ofile); int fd = fileno(ofile); assert(fd >= 0); old_stdio_ = dup(fd); assert(old_stdio_ >= 0); int new_stdio_ = open("/dev/null", O_WRONLY); assert(new_stdio_ >= 0); [[maybe_unused]] int ret = dup2(new_stdio_, fd); assert(ret >= 0); close(new_stdio_); } StdioSilencer::~StdioSilencer() { if (old_stdio_ != -1) { fflush(ofile); int fd = fileno(ofile); assert(fd >= 0); [[maybe_unused]] int ret = dup2(old_stdio_, fd); assert(ret >= 0); close(old_stdio_); old_stdio_ = -1; } } bool get_uint64_env_var(const std::string &str, uint64_t &dest) { if (const char* env_p = std::getenv(str.c_str())) { std::istringstream stringstream(env_p); if (!(stringstream >> dest)) { LOG(ERROR) << "Env var '" << str << "' did not contain a valid uint64_t, or was zero-valued."; return false; } } return true; } std::string get_pid_exe(const std::string &pid) { std::error_code ec; std_filesystem::path proc_path{ "/proc" }; proc_path /= pid; proc_path /= "exe"; if (!std_filesystem::exists(proc_path, ec) || !std_filesystem::is_symlink(proc_path, ec)) return ""; return std_filesystem::read_symlink(proc_path).string(); } std::string get_pid_exe(pid_t pid) { return get_pid_exe(std::to_string(pid)); } bool has_wildcard(const std::string &str) { return str.find("*") != std::string::npos || (str.find("[") != std::string::npos && str.find("]") != std::string::npos); } std::vector split_string(const std::string &str, char delimiter, bool remove_empty) { std::vector elems; std::stringstream ss(str); std::string value; while(std::getline(ss, value, delimiter)) { if (remove_empty && value.empty()) continue; elems.push_back(value); } return elems; } /// Erase prefix up to the first colon (:) from str and return the prefix std::string erase_prefix(std::string &str) { std::string prefix = str.substr(0, str.find(':')); str.erase(0, prefix.length() + 1); return prefix; } bool wildcard_match(const std::string &str, std::vector &tokens, bool start_wildcard, bool end_wildcard) { size_t next = 0; if (!start_wildcard) if (str.find(tokens[0], next) != next) return false; for (std::string token : tokens) { size_t found = str.find(token, next); if (found == std::string::npos) return false; next = found + token.length(); } if (!end_wildcard) if (str.length() != next) return false; return true; } std::vector get_online_cpus() { return read_cpu_range("/sys/devices/system/cpu/online"); } std::vector get_possible_cpus() { return read_cpu_range("/sys/devices/system/cpu/possible"); } std::vector get_kernel_cflags( const char* uname_machine, const std::string& ksrc, const std::string& kobj) { std::vector cflags; std::string arch = uname_machine; const char *archenv; if (!strncmp(uname_machine, "x86_64", 6)) { arch = "x86"; } else if (uname_machine[0] == 'i' && !strncmp(&uname_machine[2], "86", 2)) { arch = "x86"; } else if (!strncmp(uname_machine, "arm", 3)) { arch = "arm"; } else if (!strncmp(uname_machine, "sa110", 5)) { arch = "arm"; } else if (!strncmp(uname_machine, "s390x", 5)) { arch = "s390"; } else if (!strncmp(uname_machine, "parisc64", 8)) { arch = "parisc"; } else if (!strncmp(uname_machine, "ppc", 3)) { arch = "powerpc"; } else if (!strncmp(uname_machine, "mips", 4)) { arch = "mips"; } else if (!strncmp(uname_machine, "sh", 2)) { arch = "sh"; } else if (!strncmp(uname_machine, "aarch64", 7)) { arch = "arm64"; } // If ARCH env is defined, use it over uname archenv = getenv("ARCH"); if (archenv) arch = std::string(archenv); cflags.push_back("-nostdinc"); cflags.push_back("-isystem"); cflags.push_back("/virtual/lib/clang/include"); // see linux/Makefile for $(LINUXINCLUDE) + $(USERINCLUDE) cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include"); cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated"); cflags.push_back("-I" + ksrc + "/include"); cflags.push_back("-I" + kobj + "/include"); cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include/uapi"); cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated/uapi"); cflags.push_back("-I" + ksrc + "/include/uapi"); cflags.push_back("-I" + kobj + "/include/generated/uapi"); cflags.push_back("-include"); cflags.push_back(ksrc + "/include/linux/kconfig.h"); cflags.push_back("-D__KERNEL__"); cflags.push_back("-D__BPF_TRACING__"); cflags.push_back("-D__HAVE_BUILTIN_BSWAP16__"); cflags.push_back("-D__HAVE_BUILTIN_BSWAP32__"); cflags.push_back("-D__HAVE_BUILTIN_BSWAP64__"); cflags.push_back("-DKBUILD_MODNAME=\"bpftrace\""); // If ARCH env variable is set, pass this along. if (archenv) cflags.push_back("-D__TARGET_ARCH_" + arch); return cflags; } bool is_dir(const std::string& path) { std::error_code ec; std_filesystem::path buf{ path }; return std_filesystem::is_directory(buf, ec); } namespace { struct KernelHeaderTmpDir { KernelHeaderTmpDir(const std::string& prefix) : path{prefix + "XXXXXX"} { if (::mkdtemp(&path[0]) == nullptr) { throw std::runtime_error("creating temporary path for kheaders.tar.xz failed"); } } ~KernelHeaderTmpDir() { if (path.size() > 0) { // move_to either did not succeed or did not run, so clean up after ourselves exec_system(("rm -rf " + path).c_str()); } } void move_to(const std::string& new_path) { int err = ::rename(path.c_str(), new_path.c_str()); if (err == 0) { path = ""; } } std::string path; }; std::string unpack_kheaders_tar_xz(const struct utsname& utsname) { std::error_code ec; std_filesystem::path path_prefix{ "/tmp" }; std_filesystem::path path_kheaders{ "/sys/kernel/kheaders.tar.xz" }; if (const char* tmpdir = ::getenv("TMPDIR")) { path_prefix = tmpdir; } path_prefix /= "kheaders-"; std_filesystem::path shared_path{ path_prefix.string() + utsname.release }; if (std_filesystem::exists(shared_path, ec)) { // already unpacked return shared_path.string(); } if (!std_filesystem::exists(path_kheaders, ec)) { StderrSilencer silencer; silencer.silence(); FILE* modprobe = ::popen("modprobe kheaders", "w"); if (modprobe == nullptr || pclose(modprobe) != 0) { return ""; } if (!std_filesystem::exists(path_kheaders, ec)) { return ""; } } KernelHeaderTmpDir tmpdir{path_prefix}; FILE* tar = ::popen(("tar xf /sys/kernel/kheaders.tar.xz -C " + tmpdir.path).c_str(), "w"); if (!tar) { return ""; } int rc = ::pclose(tar); if (rc == 0) { tmpdir.move_to(shared_path); return shared_path; } return ""; } } // namespace // get_kernel_dirs returns {ksrc, kobj} - directories for pristine and // generated kernel sources. // // When the kernel was built in its source tree ksrc == kobj, however when // the kernel was build in a different directory than its source, ksrc != kobj. // // A notable example is Debian, which places pristine kernel headers in // // /lib/modules/`uname -r`/source/ // // and generated kernel headers in // // /lib/modules/`uname -r`/build/ // // {"", ""} is returned if no trace of kernel headers was found at all. // Both ksrc and kobj are guaranteed to be != "", if at least some trace of kernel sources was found. std::tuple get_kernel_dirs( const struct utsname &utsname, bool unpack_kheaders) { #ifdef KERNEL_HEADERS_DIR return {KERNEL_HEADERS_DIR, KERNEL_HEADERS_DIR}; #endif const char *kpath_env = ::getenv("BPFTRACE_KERNEL_SOURCE"); if (kpath_env) return std::make_tuple(kpath_env, kpath_env); std::string kdir = std::string("/lib/modules/") + utsname.release; auto ksrc = kdir + "/source"; auto kobj = kdir + "/build"; // if one of source/ or build/ is not present - try to use the other one for both. if (!is_dir(ksrc)) { ksrc = ""; } if (!is_dir(kobj)) { kobj = ""; } if (ksrc.empty() && kobj.empty()) { if (unpack_kheaders) { const auto kheaders_tar_xz_path = unpack_kheaders_tar_xz(utsname); if (kheaders_tar_xz_path.size() > 0) return std::make_tuple(kheaders_tar_xz_path, kheaders_tar_xz_path); } return std::make_tuple("", ""); } if (ksrc.empty()) { ksrc = kobj; } else if (kobj.empty()) { kobj = ksrc; } return std::make_tuple(ksrc, kobj); } const std::string &is_deprecated(const std::string &str) { std::vector::iterator item; for (item = DEPRECATED_LIST.begin(); item != DEPRECATED_LIST.end(); item++) { if (str == item->old_name) { if (item->show_warning) { LOG(WARNING) << item->old_name << " is deprecated and will be removed in the future. Use " << item->new_name << " instead."; item->show_warning = false; } return item->new_name; } } return str; } bool is_unsafe_func(const std::string &func_name) { return std::any_of( UNSAFE_BUILTIN_FUNCS.begin(), UNSAFE_BUILTIN_FUNCS.end(), [&](const auto& cand) { return func_name == cand; }); } bool is_compile_time_func(const std::string &func_name) { return std::any_of(COMPILE_TIME_FUNCS.begin(), COMPILE_TIME_FUNCS.end(), [&](const auto &cand) { return func_name == cand; }); } std::string exec_system(const char* cmd) { std::array buffer; std::string result; std::shared_ptr pipe(popen(cmd, "r"), pclose); if (!pipe) throw std::runtime_error("popen() failed!"); while (!feof(pipe.get())) { if (fgets(buffer.data(), 128, pipe.get()) != nullptr) result += buffer.data(); } return result; } /* Original resolve_binary_path API defaulting to bpftrace's mount namespace */ std::vector resolve_binary_path(const std::string& cmd) { const char *env_paths = getenv("PATH"); return resolve_binary_path(cmd, env_paths, -1); } /* If a pid is specified, the binary path is taken relative to its own PATH if it is in a different mount namespace. Otherwise, the path is resolved relative to the local PATH env var for bpftrace's own mount namespace if it is set */ std::vector resolve_binary_path(const std::string &cmd, int pid) { std::string env_paths = ""; std::ostringstream pid_environ_path; if (pid > 0 && pid_in_different_mountns(pid)) { pid_environ_path << "/proc/" << pid << "/environ"; std::ifstream environ(pid_environ_path.str()); if (environ) { std::string env_var; std::string pathstr = ("PATH="); while (std::getline(environ, env_var, '\0')) { if (env_var.find(pathstr) != std::string::npos) { env_paths = env_var.substr(pathstr.length()); break; } } } return resolve_binary_path(cmd, env_paths.c_str(), pid); } else { return resolve_binary_path(cmd, getenv("PATH"), pid); } } /* Private interface to resolve_binary_path, used for the exposed variants above, allowing for a PID whose mount namespace should be optionally considered. */ static std::vector resolve_binary_path(const std::string &cmd, const char *env_paths, int pid) { std::vector candidate_paths = { cmd }; if (env_paths != nullptr && cmd.find("/") == std::string::npos) for (const auto& path : split_string(env_paths, ':')) candidate_paths.push_back(path + "/" + cmd); if (cmd.find("*") != std::string::npos) candidate_paths = ::expand_wildcard_paths(candidate_paths); std::vector valid_executable_paths; for (const auto &path : candidate_paths) { std::string rel_path; if (pid > 0 && pid_in_different_mountns(pid)) rel_path = path_for_pid_mountns(pid, path); else rel_path = path; if (bcc_elf_is_exe(rel_path.c_str()) || bcc_elf_is_shared_obj(rel_path.c_str())) valid_executable_paths.push_back(rel_path); } return valid_executable_paths; } std::string path_for_pid_mountns(int pid, const std::string &path) { std::ostringstream pid_relative_path; char pid_root[64]; snprintf(pid_root, sizeof(pid_root), "/proc/%d/root", pid); if (path.find(pid_root) != 0) { std::string sep = (path.length() >= 1 && path.at(0) == '/') ? "" : "/"; pid_relative_path << pid_root << sep << path; } else { // The path is already relative to the pid's root pid_relative_path << path; } return pid_relative_path.str(); } /* Determines if the target process is in a different mount namespace from bpftrace. If a process is in a different mount namespace (eg, container) it is very likely that any references to local paths will not be valid, and that paths need to be made relative to the PID. If an invalid PID is specified or doesn't exist, it returns false. True is only returned if the namespace of the target process could be read and it doesn't match that of bpftrace. If there was an error reading either mount namespace, it will throw an exception */ static bool pid_in_different_mountns(int pid) { if (pid <= 0) return false; std::error_code ec; std_filesystem::path self_path{ "/proc/self/ns/mnt" }; std_filesystem::path target_path{ "/proc" }; target_path /= std::to_string(pid); target_path /= "ns/mnt"; if (!std_filesystem::exists(self_path, ec)) { throw MountNSException( "Failed to compare mount ns with PID " + std::to_string(pid) + ". The error was open (/proc/self/ns/mnt): " + ec.message()); } if (!std_filesystem::exists(target_path, ec)) { throw MountNSException( "Failed to compare mount ns with PID " + std::to_string(pid) + ". The error was open (/proc//ns/mnt): " + ec.message()); } bool result = !std_filesystem::equivalent(self_path, target_path, ec); if (ec) { throw MountNSException("Failed to compare mount ns with PID " + std::to_string(pid) + ". The error was (fstat): " + ec.message()); } return result; } void cat_file(const char *filename, size_t max_bytes, std::ostream &out) { std::ifstream file(filename); const size_t BUFSIZE = 4096; if (file.fail()){ LOG(ERROR) << "failed to open file '" << filename << "': " << strerror(errno); return; } char buf[BUFSIZE]; size_t bytes_read = 0; // Read the file batches to avoid allocating a potentially // massive buffer. while (bytes_read < max_bytes) { size_t size = std::min(BUFSIZE, max_bytes - bytes_read); file.read(buf, size); out.write(buf, file.gcount()); if (file.eof()) { return; } if (file.fail()) { LOG(ERROR) << "failed to open file '" << filename << "': " << strerror(errno); return; } bytes_read += file.gcount(); } } std::string str_join(const std::vector &list, const std::string &delim) { std::string str; bool first = true; for (const auto &elem : list) { if (first) first = false; else str += delim; str += elem; } return str; } bool is_numeric(const std::string &s) { std::size_t idx; try { std::stoll(s, &idx, 0); } catch (...) { return false; } return idx == s.size(); } bool symbol_has_cpp_mangled_signature(const std::string &sym_name) { if (!sym_name.rfind("_Z", 0) || !sym_name.rfind("____Z", 0)) return true; else return false; } pid_t parse_pid(const std::string &str) { try { constexpr ssize_t pid_max = 4 * 1024 * 1024; std::size_t idx = 0; auto pid = std::stol(str, &idx, 10); // Detect cases like `13ABC` if (idx < str.size()) throw InvalidPIDException(str, "is not a valid decimal number"); if (pid < 1 || pid > pid_max) throw InvalidPIDException(str, "out of valid pid range [1," + std::to_string(pid_max) + "]"); return pid; } catch (const std::out_of_range &e) { throw InvalidPIDException(str, "outside of integer range"); } catch (const std::invalid_argument &e) { throw InvalidPIDException(str, "is not a valid decimal number"); } } std::string hex_format_buffer(const char *buf, size_t size) { // Allow enough space for every byte to be sanitized in the form "\x00" char s[size * 4 + 1]; size_t offset = 0; for (size_t i = 0; i < size; i++) if (buf[i] >= 32 && buf[i] <= 126) offset += sprintf(s + offset, "%c", ((const uint8_t *)buf)[i]); else offset += sprintf(s + offset, "\\x%02x", ((const uint8_t *)buf)[i]); s[offset] = '\0'; return std::string(s); } std::unordered_set get_traceable_funcs() { #ifdef FUZZ return {}; #else // Try to get the list of functions from BPFTRACE_AVAILABLE_FUNCTIONS_TEST env const char *path = std::getenv("BPFTRACE_AVAILABLE_FUNCTIONS_TEST"); // Use kprobe list as default if (!path) path = kprobe_path.c_str(); std::ifstream available_funs(path); if (available_funs.fail()) { if (bt_debug != DebugLevel::kNone) { std::cerr << "Error while reading traceable functions from " << kprobe_path << ": " << strerror(errno); } return {}; } std::unordered_set result; std::string line; while (std::getline(available_funs, line)) { if (symbol_has_module(line)) result.insert(strip_symbol_module(line)); else result.insert(line); } return result; #endif } /** * Search for LINUX_VERSION_CODE in the vDSO, returning 0 if it can't be found. */ static uint32_t _find_version_note(unsigned long base) { auto ehdr = reinterpret_cast(base); for (int i = 0; i < ehdr->e_shnum; i++) { auto shdr = reinterpret_cast(base + ehdr->e_shoff + (i * ehdr->e_shentsize)); if (shdr->sh_type == SHT_NOTE) { auto ptr = reinterpret_cast(base + shdr->sh_offset); auto end = ptr + shdr->sh_size; while (ptr < end) { auto nhdr = reinterpret_cast(ptr); ptr += sizeof *nhdr; auto name = ptr; ptr += (nhdr->n_namesz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); auto desc = ptr; ptr += (nhdr->n_descsz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); if ((nhdr->n_namesz > 5 && !memcmp(name, "Linux", 5)) && nhdr->n_descsz == 4 && !nhdr->n_type) return *reinterpret_cast(desc); } } } return 0; } static uint32_t kernel_version_from_vdso(void) { // Fetch LINUX_VERSION_CODE from the vDSO .note section, falling back on // the build-time constant if unavailable. This always matches the // running kernel, but is not supported on arm32. unsigned code = 0; unsigned long base = getauxval(AT_SYSINFO_EHDR); if (base && !memcmp(reinterpret_cast(base), ELFMAG, 4)) code = _find_version_note(base); if (!code) code = LINUX_VERSION_CODE; return code; } static uint32_t kernel_version_from_uts(void) { struct utsname utsname; if (uname(&utsname) < 0) return 0; unsigned x, y, z; if (sscanf(utsname.release, "%u.%u.%u", &x, &y, &z) != 3) return 0; return KERNEL_VERSION(x, y, z); } static uint32_t kernel_version_from_khdr(void) { // Try to get the definition of LINUX_VERSION_CODE at runtime. std::ifstream linux_version_header{ "/usr/include/linux/version.h" }; const std::string content{ std::istreambuf_iterator( linux_version_header), std::istreambuf_iterator() }; const std::regex regex{ "#define\\s+LINUX_VERSION_CODE\\s+(\\d+)" }; std::smatch match; if (std::regex_search(content.begin(), content.end(), match, regex)) return static_cast(std::stoi(match[1])); return 0; } /** * Find a LINUX_VERSION_CODE matching the host kernel. The build-time constant * may not match if bpftrace is compiled on a different Linux version than it's * used on, e.g. if built with Docker. */ uint32_t kernel_version(int attempt) { static std::optional a0, a1, a2; switch (attempt) { case 0: { if (!a0) a0 = kernel_version_from_vdso(); return *a0; } case 1: { if (!a1) a1 = kernel_version_from_uts(); return *a1; } case 2: { if (!a2) a2 = kernel_version_from_khdr(); return *a2; } default: throw std::runtime_error("BUG: kernel_version(): Invalid attempt: " + std::to_string(attempt)); } } std::optional abs_path(const std::string &rel_path) { // filesystem::canonical does not work very well with /proc//root paths // of processes in a different mount namespace (than the one bpftrace is // running in), failing during canonicalization. See iovisor:bpftrace#1595 static auto re = std::regex("^/proc/\\d+/root/.*"); if (!std::regex_match(rel_path, re)) { try { auto p = std_filesystem::path(rel_path); return std_filesystem::canonical(std_filesystem::absolute(p)).string(); } catch (std_filesystem::filesystem_error &) { return {}; } } else { return rel_path; } } int64_t min_value(const std::vector &value, int nvalues) { int64_t val, max = 0, retval; for (int i = 0; i < nvalues; i++) { val = read_data(value.data() + i * sizeof(int64_t)); if (val > max) max = val; } /* * This is a hack really until the code generation for the min() function * is sorted out. The way it is currently implemented doesn't allow > * 32 bit quantities and also means we have to do gymnastics with the return * value owing to the way it is stored (i.e., 0xffffffff - val). */ if (max == 0) /* If we have applied the zero() function */ retval = max; else if ((0xffffffff - max) <= 0) /* A negative 32 bit value */ retval = 0 - (max - 0xffffffff); else retval = 0xffffffff - max; /* A positive 32 bit value */ return retval; } uint64_t max_value(const std::vector &value, int nvalues) { uint64_t val, max = 0; for (int i = 0; i < nvalues; i++) { val = read_data(value.data() + i * sizeof(uint64_t)); if (val > max) max = val; } return max; } bool symbol_has_module(const std::string &symbol) { return !symbol.empty() && symbol[symbol.size() - 1] == ']'; } std::string strip_symbol_module(const std::string &symbol) { size_t idx = symbol.rfind(" ["); return idx != std::string::npos ? symbol.substr(0, idx) : symbol; } } // namespace bpftrace bpftrace-0.14.0/src/utils.h000066400000000000000000000147241413460502400154710ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include namespace bpftrace { struct vmlinux_location { const char *path; // path with possible "%s" format to be replaced current // release bool raw; // file is either as ELF (false) or raw BTF data (true) }; extern const struct vmlinux_location vmlinux_locs[]; class MountNSException : public std::exception { public: MountNSException(const std::string &msg) : msg_(msg) { } const char *what() const noexcept override { return msg_.c_str(); } private: std::string msg_; }; class InvalidPIDException : public std::exception { public: InvalidPIDException(const std::string &pid, const std::string &msg) { msg_ = "pid '" + pid + "' " + msg; } const char *what() const noexcept override { return msg_.c_str(); } private: std::string msg_; }; class EnospcException : public std::runtime_error { public: // C++11 feature: bring base class constructor into scope to automatically // forward constructor calls to base class using std::runtime_error::runtime_error; }; class StdioSilencer { public: StdioSilencer() = default; ~StdioSilencer(); void silence(); protected: FILE *ofile; private: int old_stdio_ = -1; }; class StderrSilencer : public StdioSilencer { public: StderrSilencer() { ofile = stderr; } }; class StdoutSilencer : public StdioSilencer { public: StdoutSilencer() { ofile = stdout; } }; // Helper class to convert a pointer to an `std::istream` class Membuf : public std::streambuf { public: Membuf(uint8_t *begin, uint8_t *end) { auto b = reinterpret_cast(begin); auto e = reinterpret_cast(end); this->setg(b, b, e); } }; // Hack used to suppress build warning related to #474 template new_signature cast_signature(old_signature func) { #if __GNUC__ >= 8 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"") #endif return reinterpret_cast(func); #if __GNUC__ >= 8 _Pragma("GCC diagnostic pop") #endif } struct DeprecatedName { std::string old_name; std::string new_name; bool show_warning = true; }; static std::vector DEPRECATED_LIST = { }; static std::vector UNSAFE_BUILTIN_FUNCS = { "system", "signal", "override", }; static std::vector COMPILE_TIME_FUNCS = { "cgroupid" }; bool get_uint64_env_var(const ::std::string &str, uint64_t &dest); std::string get_pid_exe(pid_t pid); std::string get_pid_exe(const std::string &pid); bool has_wildcard(const std::string &str); std::vector split_string(const std::string &str, char delimiter, bool remove_empty = false); std::string erase_prefix(std::string &str); bool wildcard_match(const std::string &str, std::vector &tokens, bool start_wildcard, bool end_wildcard); std::vector get_online_cpus(); std::vector get_possible_cpus(); bool is_dir(const std::string &path); std::tuple get_kernel_dirs( const struct utsname &utsname, bool unpack_kheaders); std::vector get_kernel_cflags(const char *uname_machine, const std::string &ksrc, const std::string &kobj); std::unordered_set get_traceable_funcs(); const std::string &is_deprecated(const std::string &str); bool is_unsafe_func(const std::string &func_name); bool is_compile_time_func(const std::string &func_name); std::string exec_system(const char *cmd); std::vector resolve_binary_path(const std::string &cmd); std::vector resolve_binary_path(const std::string &cmd, int pid); std::string path_for_pid_mountns(int pid, const std::string &path); void cat_file(const char *filename, size_t, std::ostream &); std::string str_join(const std::vector &list, const std::string &delim); bool is_numeric(const std::string &str); bool symbol_has_cpp_mangled_signature(const std::string &sym_name); pid_t parse_pid(const std::string &str); std::string hex_format_buffer(const char *buf, size_t size); std::optional abs_path(const std::string &rel_path); bool symbol_has_module(const std::string &symbol); std::string strip_symbol_module(const std::string &symbol); // Generate object file section name for a given probe inline std::string get_section_name_for_probe( const std::string &probe_name, int index, std::optional usdt_location_index = std::nullopt) { auto ret = "s_" + probe_name; if (usdt_location_index) ret += "_loc" + std::to_string(*usdt_location_index); ret += "_" + std::to_string(index); return ret; } inline std::string get_watchpoint_setup_probe_name( const std::string &probe_name) { return probe_name + "_wp_setup"; } inline std::string get_section_name_for_watchpoint_setup( const std::string &probe_name, int index) { return get_section_name_for_probe(get_watchpoint_setup_probe_name(probe_name), index); } // trim from end of string (right) inline std::string &rtrim(std::string &s) { s.erase(s.find_last_not_of(" ") + 1); return s; } // trim from beginning of string (left) inline std::string <rim(std::string &s) { s.erase(0, s.find_first_not_of(" ")); return s; } // trim from both ends of string (right then left) inline std::string &trim(std::string &s) { return ltrim(rtrim(s)); } template T read_data(const void *src) { T v; std::memcpy(&v, src, sizeof(v)); return v; } uint32_t kernel_version(int attempt); template T reduce_value(const std::vector &value, int nvalues) { T sum = 0; for (int i = 0; i < nvalues; i++) { sum += read_data(value.data() + i * sizeof(T)); } return sum; } int64_t min_value(const std::vector &value, int nvalues); uint64_t max_value(const std::vector &value, int nvalues); // Combination of 2 hashes // The algorithm is taken from boost::hash_combine template inline void hash_combine(std::size_t &seed, const T &value) { std::hash hasher; seed ^= hasher(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } } // namespace bpftrace bpftrace-0.14.0/tests/000077500000000000000000000000001413460502400145235ustar00rootroot00000000000000bpftrace-0.14.0/tests/CMakeLists.txt000066400000000000000000000212371413460502400172700ustar00rootroot00000000000000add_compile_options("-Wno-undef") if (STATIC_LIBC) # STATIC_LIBC will set linker flags to -static. We don't want this for # testprogs linking, so we clear the flags. unset(CMAKE_EXE_LINKER_FLAGS) endif() # Regenerate codegen_includes.cpp whenever anything in the tests/codegen # directory changes file(GLOB_RECURSE CODEGEN_SOURCES codegen/*.cpp codegen/*.h) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/tests/codegen_includes.cpp COMMAND ${CMAKE_COMMAND} -DTEST_SRC_DIR="${CMAKE_SOURCE_DIR}/tests/codegen" -P ${CMAKE_SOURCE_DIR}/tests/codegen/generate_codegen_includes.cmake DEPENDS ${CODEGEN_SOURCES}) if(${LLVM_VERSION_MAJOR} VERSION_EQUAL 12) set(CODEGEN_SRC ${CMAKE_BINARY_DIR}/tests/codegen_includes.cpp) else() set(CODEGEN_SRC "") message(STATUS "Disabled codegen test for LLVM != 12") endif() add_executable(bpftrace_test ast.cpp bpftrace.cpp child.cpp clang_parser.cpp log.cpp main.cpp mocks.cpp parser.cpp portability_analyser.cpp procmon.cpp probe.cpp required_resources.cpp semantic_analyser.cpp tracepoint_format_parser.cpp utils.cpp ${CODEGEN_SRC} ) # Regenerate DWARF data for unit tests add_custom_target(testing_debuginfo_data COMMAND ${CMAKE_COMMAND} -DDATA_SOURCE_DIR="${CMAKE_SOURCE_DIR}/tests/data" -P ${CMAKE_SOURCE_DIR}/tests/data/generate_debuginfo_data.cmake) add_dependencies(bpftrace_test testing_debuginfo_data) target_compile_definitions(bpftrace_test PRIVATE TEST_CODEGEN_LOCATION="${CMAKE_SOURCE_DIR}/tests/codegen/llvm/") target_link_libraries(bpftrace_test libbpftrace) if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") target_compile_definitions(bpftrace_test PRIVATE ARCH_AARCH64) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le") target_compile_definitions(bpftrace_test PRIVATE ARCH_PPC64) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "s390" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "s390x") target_compile_definitions(bpftrace_test PRIVATE ARCH_S390) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") target_compile_definitions(bpftrace_test PRIVATE ARCH_X86_64) endif() target_compile_definitions(bpftrace_test PRIVATE ${BPFTRACE_FLAGS}) if(BUILD_ASAN) target_compile_options(bpftrace_test PUBLIC "-fsanitize=address") target_link_options(bpftrace_test PUBLIC "-fsanitize=address") endif() if(STATIC_LINKING) if(EMBED_USE_LLVM) target_link_options(bpftrace_test BEFORE PRIVATE "-static-libgcc" "-static-libstdc++") endif() if(STATIC_LIBC) set_target_properties(bpftrace_test PROPERTIES LINK_FLAGS "-static") endif() endif(STATIC_LINKING) # Still need to support vendored gtest/gmock b/c old ubuntu versions (< focal) # only provide gtest/gmock sources -- no precompiled binaries if(VENDOR_GTEST) # Ninja build system needs the byproducts set explicilty so it can # check for missing dependencies. # https://cmake.org/pipermail/cmake/2015-April/060234.html set(gtest_byproducts /googlemock/gtest/libgtest.a /googlemock/gtest/libgtest_main.a /googlemock/libgmock.a ) include(ExternalProject) ExternalProject_Add(gtest-git GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.11.0 STEP_TARGETS build update EXCLUDE_FROM_ALL 1 BUILD_BYPRODUCTS ${gtest_byproducts} ) add_dependencies(bpftrace_test gtest-git-build) ExternalProject_Get_Property(gtest-git source_dir binary_dir) target_include_directories(bpftrace_test PUBLIC ${source_dir}/googletest/include) target_include_directories(bpftrace_test PUBLIC ${source_dir}/googlemock/include) target_link_libraries(bpftrace_test ${binary_dir}/lib/libgtest.a) target_link_libraries(bpftrace_test ${binary_dir}/lib/libgtest_main.a) target_link_libraries(bpftrace_test ${binary_dir}/lib/libgmock.a) else() # bpftrace tests require (at minimum) the vendored version (see above). # There's no great way to enforce a minimum version from cmake -- the cmake # modules don't respect minimum requested version. find_package(GTest REQUIRED) find_package(GMock REQUIRED) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}) target_link_libraries(bpftrace_test ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARIES}) endif(VENDOR_GTEST) add_test(NAME bpftrace_test COMMAND bpftrace_test) if(NOT STATIC_LINKING) find_package(Threads REQUIRED) target_link_libraries(bpftrace_test ${CMAKE_THREAD_LIBS_INIT}) endif(NOT STATIC_LINKING) # Compile all testprograms, one per .c file for runtime testing file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testprogs/) file(GLOB testprogs testprogs/*.c) set(compiled_testprogs "") foreach(testprog ${testprogs}) get_filename_component(bin_name ${testprog} NAME_WE) add_executable (${bin_name} ${testprog}) if(HAVE_SYSTEMTAP_SYS_SDT_H) target_compile_definitions(${bin_name} PRIVATE HAVE_SYSTEMTAP_SYS_SDT_H) endif(HAVE_SYSTEMTAP_SYS_SDT_H) set_target_properties(${bin_name} PROPERTIES LINK_SEARCH_START_STATIC FALSE LINK_SEARCH_END_STATIC FALSE RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testprogs/ COMPILE_FLAGS "-g -O0" LINK_FLAGS "-no-pie") list(APPEND compiled_testprogs ${CMAKE_CURRENT_BINARY_DIR}/testprogs/${bin_name}) endforeach() add_custom_target(testprogs DEPENDS ${compiled_testprogs}) target_include_directories(usdt_lib PUBLIC ${CMAKE_SOURCE_DIR}/tests/testlibs/) target_compile_options(usdt_lib PRIVATE -fPIC) target_link_libraries(usdt_lib usdt_tp) # Similarly compile all test libs file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testlibs/) file(GLOB testlibs testlibs/*.c) set(compiled_testlibs "") foreach(testlib_source ${testlibs}) get_filename_component(testlib_name ${testlib_source} NAME_WE) add_library(${testlib_name} SHARED ${testlib_source}) set_target_properties(${testlib_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testlibs/ COMPILE_FLAGS "-g -O0") if(HAVE_SYSTEMTAP_SYS_SDT_H) target_compile_definitions(${testlib_name} PRIVATE HAVE_SYSTEMTAP_SYS_SDT_H) endif(HAVE_SYSTEMTAP_SYS_SDT_H) # clear the executable bit add_custom_command(TARGET ${testlib_name} POST_BUILD COMMAND chmod -x ${CMAKE_CURRENT_BINARY_DIR}/testlibs/lib${testlib_name}.so) list(APPEND compiled_testlibs ${CMAKE_CURRENT_BINARY_DIR}/testlibs/lib${testlib_name}.so) endforeach() add_custom_target(testlibs DEPENDS ${compiled_testlibs}) configure_file(runtime-tests.sh runtime-tests.sh COPYONLY) file(GLOB runtime_tests runtime/*) list(REMOVE_ITEM runtime_tests ${CMAKE_CURRENT_SOURCE_DIR}/runtime/engine) list(REMOVE_ITEM runtime_tests ${CMAKE_CURRENT_SOURCE_DIR}/runtime/scripts) list(REMOVE_ITEM runtime_tests ${CMAKE_CURRENT_SOURCE_DIR}/runtime/outputs) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/runtime/) foreach(runtime_test ${runtime_tests}) configure_file(${runtime_test} ${CMAKE_CURRENT_BINARY_DIR}/runtime/ COPYONLY) endforeach() file(GLOB runtime_engine_files runtime/engine/*) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/runtime/engine) foreach(runtime_engine_file ${runtime_engine_files}) configure_file(${runtime_engine_file} ${CMAKE_CURRENT_BINARY_DIR}/runtime/engine/ @ONLY) endforeach() file(GLOB runtime_test_scripts runtime/scripts/*) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/runtime/scripts) foreach(runtime_test_script ${runtime_test_scripts}) configure_file(${runtime_test_script} ${CMAKE_CURRENT_BINARY_DIR}/runtime/scripts/ COPYONLY) endforeach() file(GLOB runtime_test_outputs runtime/outputs/*) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/runtime/outputs) foreach(runtime_test_output ${runtime_test_outputs}) configure_file(${runtime_test_output} ${CMAKE_CURRENT_BINARY_DIR}/runtime/outputs/ COPYONLY) endforeach() add_custom_target( runtime-tests COMMAND ./runtime-tests.sh DEPENDS ${compiled_testprogs} ${compiled_testlibs} ${CMAKE_BINARY_DIR}/src/bpftrace ) add_test(NAME runtime_test COMMAND ./runtime-tests.sh) configure_file(tools-parsing-test.sh tools-parsing-test.sh COPYONLY) add_custom_target(tools-parsing-test COMMAND ./tools-parsing-test.sh) add_test(NAME tools-parsing-test COMMAND ./tools-parsing-test.sh) if (BUILD_ASAN) configure_file(memleak-tests.sh memleak-tests.sh COPYONLY) add_custom_target(memleak-test COMMAND ./memleak-tests.sh) endif() if(ENABLE_TEST_VALIDATE_CODEGEN) if(${LLVM_VERSION_MAJOR} VERSION_EQUAL 12) message(STATUS "Adding codegen-validator test") configure_file(codegen-validator.sh codegen-validator.sh COPYONLY) add_custom_target(codegen-validator COMMAND ./codegen-validator.sh) add_test(NAME codegen-validator COMMAND ./codegen-validator.sh ${CMAKE_SOURCE_DIR}) else() message(STATUS "Not building with LLVM 12, skipping codegen-validator test") endif() else() message(STATUS "codegen-validator test disabled") endif() bpftrace-0.14.0/tests/README.md000066400000000000000000000116711413460502400160100ustar00rootroot00000000000000# bpftrace Tests There are two test suites in the project. ## Unit tests These tests can be run with the `bpftrace_test` executable. ### Codegen tests The codegen tests verify that the optimized IR matches our expectations. The tests are defined as C++ files in the `tests/codegen` directory and look like: ``` TEST(codegen, call_avg) { test("kprobe:f { @x = avg(pid) }", NAME); } ``` The `test` function does all the heavy lifting and is defined in `tests/codegen/common.h`. It compiles the specified program (first argument) and compares it (string compare) with the expected result, a file named by the second argument. The `NAME` macro holds the test name, which is `call_avg` in this case. #### Updating Run `./scripts/update_codegen_tests.sh` after making codegen changes up update the expected LLVM IR. Alternatively (if you need more control over which tests are updated), if the test is run with `BPFTRACE_UPDATE_TESTS=1` the `test` helper will update the IR instead of running the tests. ## Runtime tests Runtime tests will call the bpftrace executable. * Run: `sudo make runtime-tests` inside your build folder * By default, runtime-tests will look for the executable in the build folder. You can set a value to the environment variable `BPFTRACE_RUNTIME_TEST_EXECUTABLE` to customize it Runtime tests are grouped into "suites". A suite is usually a single file. The name of the file is the name of the suite. ### Runtime test directives Each runtime testcase consists of multiple directives. In no particular order: * `NAME`: Name of the test case. This field is required. * `RUN`: Run the command in a shell. See "Runtime variables" below for available placeholders. This XOR the `PROG` field is required * `PROG`: Run the provided bpftrace program. This directive is preferred over `RUN` unless you must pass flags or create a shell pipeline. This XOR the `RUN` field is required * `EXPECT`: The expected output. Python regular expressions are supported. This field is required. * `TIMEOUT`: The timeout for the testcase (in seconds). This field is required. * `BEFORE`: Run the command in a shell before running bpftrace. The command will be terminated after testcase is over. * `AFTER`: Run the command in a shell after running bpftrace. The command will be terminated after the testcase is over. * `MIN_KERNEL`: Skip the test unless the host's kernel version is >= the provided kernel version. Try not to use this directive as kernel versions may be misleading (backported kernel features, for example) * `REQUIREMENT`: Run a command in a shell. If it succeeds, run the testcase. Else, skip the testcase. * `ENV`: Run bpftrace invocation with additional environment variables. Must be in format NAME=VALUE. Supports multiple values separated by spaces. * `ARCH`: Only run testcase on provided architectures. Supports `|` to logical OR multiple arches. * `REQUIRES_FEATURE`: Only run testcase if the following bpftrace feature is built in. See `bpftrace --info` and `runtime/engine/runner.py` for more details. Also supports negative features (by prefixing `!` before feature). If you need to run a test program to probe (eg, uprobe/USDT), you can use the `BEFORE` clause. The test scripts will wait for the test program to have a pid. The BEFORE clause will block up to the TIMEOUT waiting for a PID matching the basename of the last space-separated token. For instance, if the BEFORE clause is `./testprogs/usdt_test`, it will wait for a processed called `usdt_test`. If it is `./testprogs/mountns_wrapper usdt_test` it will also wait for a process called `usdt_test`. This approach is invalidated if a test program requires arguments in the future, but so far test programs are simple and separate minimal programs to test tracing functionality, and argument passing hasn't been required. If test programs need arguments, a more sophisticated approach will be necessary. ### Runtime variables Runtime variables are placeholders that the runtime test engine will fill out before running the test. These exist b/c the values of the variables are generally not known until test time. The following runtime variables are available for the `RUN` directive: * `{{BPFTRACE}}`: Path to bpftrace executable * `{{BPFTRACE_AOTRT}}`: Path to bpftrace ahead-of-time runtime executable * `{{BEFORE_PID}}`: Process ID of the process in `BEFORE` directive ### Test programs You can add test programs for your runtime tests by placing a `.c` file corresponding to your test program in `tests/testprogs`. You can add test libraries for your runtime tests by placing a `.c` file corresponding to your test library in `tests/testlibs`. The test file `tests/testprogs/my_test.c` will result in an executable that you can call and probe in your runtime test at `./testprogs/my_test` This is intended to be useful for testing uprobes and USDT probes, or using uprobes to verify some other behavior in bpftrace. It can also be used to tightly control what code paths are triggered in the system. bpftrace-0.14.0/tests/ast.cpp000066400000000000000000000110721413460502400160170ustar00rootroot00000000000000#include "ast/ast.h" #include "gtest/gtest.h" namespace bpftrace { namespace test { namespace ast { using bpftrace::ast::AttachPoint; using bpftrace::ast::AttachPointList; using bpftrace::ast::Probe; static AttachPointList *APL(std::vector list) { auto apl = new AttachPointList(); for (auto l : list) apl->push_back(l->leafcopy()); return apl; } TEST(ast, probe_name_special) { auto ap1 = new AttachPoint(""); ap1->provider = "BEGIN"; auto attach_points1 = APL({ ap1 }); Probe begin(attach_points1, nullptr, nullptr); EXPECT_EQ(begin.name(), "BEGIN"); auto ap2 = new AttachPoint(""); ap2->provider = "END"; auto attach_points2 = APL({ ap2 }); Probe end(attach_points2, nullptr, nullptr); EXPECT_EQ(end.name(), "END"); } TEST(ast, probe_name_kprobe) { auto ap1 = new AttachPoint(""); ap1->provider = "kprobe"; ap1->func = "sys_read"; auto ap2 = new AttachPoint(""); ap2->provider = "kprobe"; ap2->func = "sys_write"; auto ap3 = new AttachPoint(""); ap3->provider = "kprobe"; ap3->func = "sys_read"; ap3->func_offset = 10; auto attach_points1 = APL({ ap1 }); Probe kprobe1(attach_points1, nullptr, nullptr); EXPECT_EQ(kprobe1.name(), "kprobe:sys_read"); auto ap2_copy1 = ap2->leafcopy(); auto attach_points2 = APL({ ap1, ap2 }); Probe kprobe2(attach_points2, nullptr, nullptr); EXPECT_EQ(kprobe2.name(), "kprobe:sys_read,kprobe:sys_write"); auto attach_points3 = APL({ ap1, ap2_copy1, ap3 }); Probe kprobe3(attach_points3, nullptr, nullptr); EXPECT_EQ(kprobe3.name(), "kprobe:sys_read,kprobe:sys_write,kprobe:sys_read+10"); } TEST(ast, probe_name_uprobe) { auto ap1 = new AttachPoint(""); ap1->provider = "uprobe"; ap1->target = "/bin/sh"; ap1->func = "readline"; auto attach_points1 = APL({ ap1 }); Probe uprobe1(attach_points1, nullptr, nullptr); EXPECT_EQ(uprobe1.name(), "uprobe:/bin/sh:readline"); auto ap2 = new AttachPoint(""); ap2->provider = "uprobe"; ap2->target = "/bin/sh"; ap2->func = "somefunc"; auto attach_points2 = APL({ ap1, ap2 }); Probe uprobe2(attach_points2, nullptr, nullptr); EXPECT_EQ(uprobe2.name(), "uprobe:/bin/sh:readline,uprobe:/bin/sh:somefunc"); auto ap3 = new AttachPoint(""); ap3->provider = "uprobe"; ap3->target = "/bin/sh"; ap3->address = 1000; auto attach_points3 = APL({ ap1, ap2, ap3 }); Probe uprobe3(attach_points3, nullptr, nullptr); EXPECT_EQ(uprobe3.name(), "uprobe:/bin/sh:readline,uprobe:/bin/sh:somefunc,uprobe:/bin/sh:1000"); auto ap4 = new AttachPoint(""); ap4->provider = "uprobe"; ap4->target = "/bin/sh"; ap4->func = "somefunc"; ap4->func_offset = 10; auto attach_points4 = APL({ ap1, ap2, ap3, ap4 }); Probe uprobe4(attach_points4, nullptr, nullptr); EXPECT_EQ(uprobe4.name(), "uprobe:/bin/sh:readline,uprobe:/bin/sh:somefunc,uprobe:/bin/sh:1000,uprobe:/bin/sh:somefunc+10"); auto ap5 = new AttachPoint(""); ap5->provider = "uprobe"; ap5->target = "/bin/sh"; ap5->address = 10; auto attach_points5 = APL({ ap5 }); Probe uprobe5(attach_points5, nullptr, nullptr); EXPECT_EQ(uprobe5.name(), "uprobe:/bin/sh:10"); auto ap6 = new AttachPoint(""); ap6->provider = "uretprobe"; ap6->target = "/bin/sh"; ap6->address = 10; auto attach_points6 = APL({ ap6 }); Probe uprobe6(attach_points6, nullptr, nullptr); EXPECT_EQ(uprobe6.name(), "uretprobe:/bin/sh:10"); } TEST(ast, probe_name_usdt) { auto ap1 = new AttachPoint(""); ap1->provider = "usdt"; ap1->target = "/bin/sh"; ap1->func = "probe1"; auto attach_points1 = APL({ ap1 }); Probe usdt1(attach_points1, nullptr, nullptr); EXPECT_EQ(usdt1.name(), "usdt:/bin/sh:probe1"); auto ap2 = new AttachPoint(""); ap2->provider = "usdt"; ap2->target = "/bin/sh"; ap2->func = "probe2"; auto attach_points2 = APL({ ap1, ap2 }); Probe usdt2(attach_points2, nullptr, nullptr); EXPECT_EQ(usdt2.name(), "usdt:/bin/sh:probe1,usdt:/bin/sh:probe2"); } TEST(ast, attach_point_name) { auto ap1 = new AttachPoint(""); ap1->provider = "kprobe"; ap1->func = "sys_read"; auto ap2 = new AttachPoint(""); ap2->provider = "kprobe"; ap2->func = "sys_thisone"; auto ap3 = new AttachPoint(""); ap3->provider = "uprobe"; ap3->target = "/bin/sh"; ap3->func = "readline"; auto attach_points = APL({ ap1, ap2, ap3 }); Probe kprobe(attach_points, nullptr, nullptr); EXPECT_EQ(ap2->name("sys_thisone"), "kprobe:sys_thisone"); attach_points = APL({ ap1, ap2, ap3 }); Probe uprobe(attach_points, nullptr, nullptr); EXPECT_EQ(ap3->name("readline"), "uprobe:/bin/sh:readline"); } } // namespace ast } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/bpftrace.cpp000066400000000000000000000755131413460502400170300ustar00rootroot00000000000000#include #include "bpftrace.h" #include "driver.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace bpftrace { namespace test { namespace bpftrace { using ::testing::ContainerEq; using ::testing::StrictMock; static const std::string kprobe_name(const std::string &attach_point, uint64_t func_offset) { auto str = func_offset ? "+" + std::to_string(func_offset) : ""; return "kprobe:" + attach_point + str; } void check_kprobe(Probe &p, const std::string &attach_point, const std::string &orig_name, uint64_t func_offset = 0) { EXPECT_EQ(ProbeType::kprobe, p.type); EXPECT_EQ(attach_point, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(kprobe_name(attach_point, func_offset), p.name); EXPECT_EQ(func_offset, p.func_offset); } static auto make_probe(std::vector elems) { auto apl = new ast::AttachPointList(elems); return new ast::Probe(apl, nullptr, nullptr); } static auto make_usdt_probe(const std::string &target, const std::string &ns, const std::string &func, bool need_expansion = false, int locations = 0) { auto a = new ast::AttachPoint(""); a->provider = "usdt"; a->target = target; a->ns = ns; a->func = func; a->need_expansion = need_expansion; a->usdt.num_locations = locations; return make_probe({ a }); } static auto parse_probe(const std::string &str) { StrictMock b; Driver d(b); if (d.parse_str(str)) { throw std::runtime_error("Parser failed"); } auto probe = d.root_->probes->front(); d.root_->probes->clear(); return probe; } static const std::string uprobe_name(const std::string &path, const std::string &attach_point, uint64_t address, uint64_t func_offset, bool retprobe = false) { auto provider = retprobe ? "uretprobe:" : "uprobe:"; if (attach_point.empty()) { return provider + path + ":" + std::to_string(address); } else { auto str = func_offset ? "+" + std::to_string(func_offset) : ""; return provider + path + ":" + attach_point + str; } } void check_uprobe(Probe &p, const std::string &path, const std::string &attach_point, const std::string &orig_name, uint64_t address = 0, uint64_t func_offset = 0) { bool retprobe = orig_name.find("uretprobe:") == 0 || orig_name.find("ur:") == 0; EXPECT_EQ(retprobe ? ProbeType::uretprobe : ProbeType::uprobe, p.type); EXPECT_EQ(attach_point, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(uprobe_name(path, attach_point, address, func_offset, retprobe), p.name); EXPECT_EQ(address, p.address); EXPECT_EQ(func_offset, p.func_offset); } void check_usdt(Probe &p, const std::string &path, const std::string &provider, const std::string &attach_point, const std::string &orig_name) { EXPECT_EQ(ProbeType::usdt, p.type); EXPECT_EQ(attach_point, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("usdt:" + path + ":" + provider + ":" + attach_point, p.name); } void check_tracepoint(Probe &p, const std::string &target, const std::string &func, const std::string &orig_name) { EXPECT_EQ(ProbeType::tracepoint, p.type); EXPECT_EQ(func, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("tracepoint:" + target + ":" + func, p.name); } void check_profile(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::profile, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("profile:" + unit + ":" + std::to_string(freq), p.name); } void check_interval(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::interval, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("interval:" + unit + ":" + std::to_string(freq), p.name); } void check_software(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::software, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("software:" + unit + ":" + std::to_string(freq), p.name); } void check_hardware(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::hardware, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("hardware:" + unit + ":" + std::to_string(freq), p.name); } void check_special_probe(Probe &p, const std::string &attach_point, const std::string &orig_name) { EXPECT_EQ(ProbeType::uprobe, p.type); EXPECT_EQ(attach_point, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(orig_name, p.name); } TEST(bpftrace, add_begin_probe) { ast::Probe *probe = parse_probe("BEGIN{}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(0U, bpftrace.get_probes().size()); ASSERT_EQ(1U, bpftrace.get_special_probes().size()); check_special_probe(bpftrace.get_special_probes().at(0), "BEGIN_trigger", "BEGIN"); } TEST(bpftrace, add_end_probe) { ast::Probe *probe = parse_probe("END{}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(0U, bpftrace.get_probes().size()); ASSERT_EQ(1U, bpftrace.get_special_probes().size()); check_special_probe(bpftrace.get_special_probes().at(0), "END_trigger", "END"); } TEST(bpftrace, add_probes_single) { ast::Probe *probe = parse_probe("kprobe:sys_read {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_kprobe(bpftrace.get_probes().at(0), "sys_read", "kprobe:sys_read"); } TEST(bpftrace, add_probes_multiple) { ast::Probe *probe = parse_probe("kprobe:sys_read,kprobe:sys_write{}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(2U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:sys_write"; check_kprobe(bpftrace.get_probes().at(0), "sys_read", probe_orig_name); check_kprobe(bpftrace.get_probes().at(1), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_wildcard) { ast::Probe *probe = parse_probe( "kprobe:sys_read,kprobe:my_*,kprobe:sys_write{}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file( "/sys/kernel/debug/tracing/available_filter_functions")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(4U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:my_*,kprobe:sys_write"; check_kprobe(bpftrace->get_probes().at(0), "sys_read", probe_orig_name); check_kprobe(bpftrace->get_probes().at(1), "my_one", probe_orig_name); check_kprobe(bpftrace->get_probes().at(2), "my_two", probe_orig_name); check_kprobe(bpftrace->get_probes().at(3), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_wildcard_no_matches) { ast::Probe *probe = parse_probe( "kprobe:sys_read,kprobe:not_here_*,kprobe:sys_write{}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file( "/sys/kernel/debug/tracing/available_filter_functions")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:not_here_*,kprobe:sys_write"; check_kprobe(bpftrace->get_probes().at(0), "sys_read", probe_orig_name); check_kprobe(bpftrace->get_probes().at(1), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_kernel_module) { ast::Probe *probe = parse_probe("kprobe:func_in_mod{}"); auto bpftrace = get_strict_mock_bpftrace(); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:func_in_mod"; check_kprobe(bpftrace->get_probes().at(0), "func_in_mod", probe_orig_name); } TEST(bpftrace, add_probes_kernel_module_wildcard) { ast::Probe *probe = parse_probe("kprobe:func_in_mo*{}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file( "/sys/kernel/debug/tracing/available_filter_functions")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:func_in_mo*"; check_kprobe(bpftrace->get_probes().at(0), "func_in_mod", probe_orig_name); } TEST(bpftrace, add_probes_offset) { auto offset = 10; ast::Probe *probe = parse_probe("kprobe:sys_read+10{}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read+" + std::to_string(offset); check_kprobe( bpftrace.get_probes().at(0), "sys_read", probe_orig_name, offset); } TEST(bpftrace, add_probes_uprobe) { StrictMock bpftrace; ast::Probe *probe = parse_probe("uprobe:/bin/sh:foo {}"); ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_uprobe(bpftrace.get_probes().at(0), "/bin/sh", "foo", "uprobe:/bin/sh:foo"); } TEST(bpftrace, add_probes_uprobe_wildcard) { ast::Probe *probe = parse_probe("uprobe:/bin/sh:*open {}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file("/bin/sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "uprobe:/bin/sh:*open"; check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "first_open", probe_orig_name); check_uprobe(bpftrace->get_probes().at(1), "/bin/sh", "second_open", probe_orig_name); } TEST(bpftrace, add_probes_uprobe_wildcard_file) { ast::Probe *probe = parse_probe("uprobe:/bin/*sh:first_open {}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file("/bin/*sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "uprobe:/bin/*sh:first_open"; check_uprobe( bpftrace->get_probes().at(0), "/bin/bash", "first_open", probe_orig_name); check_uprobe( bpftrace->get_probes().at(1), "/bin/sh", "first_open", probe_orig_name); } TEST(bpftrace, add_probes_uprobe_wildcard_no_matches) { ast::Probe *probe = parse_probe("uprobe:/bin/sh:foo* {}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file("/bin/sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(0U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); } TEST(bpftrace, add_probes_uprobe_string_literal) { auto a = new ast::AttachPoint(""); a->provider = "uprobe"; a->target = "/bin/sh"; a->func = "foo*"; auto probe = make_probe({ a }); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_uprobe(bpftrace.get_probes().at(0), "/bin/sh", "foo*", "uprobe:/bin/sh:foo*"); } TEST(bpftrace, add_probes_uprobe_address) { ast::Probe *probe = parse_probe("uprobe:/bin/sh:1024 {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_uprobe(bpftrace.get_probes().at(0), "/bin/sh", "", "uprobe:/bin/sh:1024", 1024); } TEST(bpftrace, add_probes_uprobe_string_offset) { ast::Probe *probe = parse_probe("uprobe:/bin/sh:foo+10{}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_uprobe(bpftrace.get_probes().at(0), "/bin/sh", "foo", "uprobe:/bin/sh:foo+10", 0, 10); } TEST(bpftrace, add_probes_uprobe_cpp_symbol) { for (auto &provider : { "uprobe", "uretprobe" }) { std::stringstream prog; prog << provider << ":/bin/sh:cpp_mangled{}"; ast::Probe *probe = parse_probe(prog.str()); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file("/bin/sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); auto orig_name = std::string(provider) + ":/bin/sh:cpp_mangled"; check_uprobe( bpftrace->get_probes().at(0), "/bin/sh", "_Z11cpp_mangledi", orig_name); check_uprobe( bpftrace->get_probes().at(1), "/bin/sh", "_Z11cpp_mangledv", orig_name); } } TEST(bpftrace, add_probes_uprobe_cpp_symbol_full) { auto probe = parse_probe("uprobe:/bin/sh:\"cpp_mangled(int)\"{}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file("/bin/sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "_Z11cpp_mangledi", "uprobe:/bin/sh:cpp_mangled(int)"); } TEST(bpftrace, add_probes_uprobe_cpp_symbol_wildcard) { auto probe = parse_probe("uprobe:/bin/sh:cpp_man*{}"); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file("/bin/sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "_Z11cpp_mangledi", "uprobe:/bin/sh:cpp_man*"); check_uprobe(bpftrace->get_probes().at(1), "/bin/sh", "_Z11cpp_mangledv", "uprobe:/bin/sh:cpp_man*"); } TEST(bpftrace, add_probes_usdt) { auto probe = parse_probe("usdt:/bin/sh:prov1:mytp{}"); probe->attach_points->front()->usdt.num_locations = 1; StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_usdt(bpftrace.get_probes().at(0), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); } TEST(bpftrace, add_probes_usdt_wildcard) { auto probe = make_usdt_probe("/bin/*sh", "prov*", "tp*", true, 1); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_usdt(0, "/bin/*sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(4U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_usdt(bpftrace->get_probes().at(0), "/bin/bash", "prov1", "tp3", "usdt:/bin/bash:prov1:tp3"); check_usdt(bpftrace->get_probes().at(1), "/bin/sh", "prov1", "tp1", "usdt:/bin/sh:prov1:tp1"); check_usdt(bpftrace->get_probes().at(2), "/bin/sh", "prov1", "tp2", "usdt:/bin/sh:prov1:tp2"); check_usdt(bpftrace->get_probes().at(3), "/bin/sh", "prov2", "tp", "usdt:/bin/sh:prov2:tp"); } TEST(bpftrace, add_probes_usdt_empty_namespace) { auto probe = make_usdt_probe("/bin/sh", "", "tp1", true, 1); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_usdt(0, "/bin/sh")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_usdt(bpftrace->get_probes().at(0), "/bin/sh", "prov1", "tp1", "usdt:/bin/sh:prov1:tp1"); } TEST(bpftrace, add_probes_usdt_empty_namespace_conflict) { auto probe = make_usdt_probe("/bin/sh", "", "tp", true, 1); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_usdt(0, "/bin/sh")) .Times(1); ASSERT_EQ(1, bpftrace->add_probe(*probe)); } TEST(bpftrace, add_probes_usdt_duplicate_markers) { auto probe = make_usdt_probe("/bin/sh", "prov1", "mytp", false, 3); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(3U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_usdt(bpftrace.get_probes().at(0), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); check_usdt(bpftrace.get_probes().at(1), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); check_usdt(bpftrace.get_probes().at(2), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); } TEST(bpftrace, add_probes_tracepoint) { auto probe = parse_probe(("tracepoint:sched:sched_switch {}")); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "tracepoint:sched:sched_switch"; check_tracepoint(bpftrace.get_probes().at(0), "sched", "sched_switch", probe_orig_name); } TEST(bpftrace, add_probes_tracepoint_wildcard) { auto probe = parse_probe(("tracepoint:sched:sched_* {}")); auto bpftrace = get_strict_mock_bpftrace(); std::set matches = { "sched_one", "sched_two" }; EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file( "/sys/kernel/debug/tracing/available_events")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "tracepoint:sched:sched_*"; check_tracepoint(bpftrace->get_probes().at(0), "sched", "sched_one", probe_orig_name); check_tracepoint(bpftrace->get_probes().at(1), "sched", "sched_two", probe_orig_name); } TEST(bpftrace, add_probes_tracepoint_category_wildcard) { auto probe = parse_probe(("tracepoint:sched*:sched_* {}")); auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file( "/sys/kernel/debug/tracing/available_events")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "tracepoint:sched*:sched_*"; check_tracepoint( bpftrace->get_probes().at(0), "sched", "sched_one", probe_orig_name); check_tracepoint( bpftrace->get_probes().at(1), "sched", "sched_two", probe_orig_name); check_tracepoint(bpftrace->get_probes().at(2), "sched_extra", "sched_extra", probe_orig_name); } TEST(bpftrace, add_probes_tracepoint_wildcard_no_matches) { auto probe = parse_probe("tracepoint:type:typo_* {}"); /* ast::AttachPoint a(""); a.provider = "tracepoint"; a.target = "typo"; a.func = "typo_*"; a.need_expansion = true; ast::AttachPointList attach_points = { &a }; ast::Probe probe(&attach_points, nullptr, nullptr); */ auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file( "/sys/kernel/debug/tracing/available_events")) .Times(1); ASSERT_EQ(0, bpftrace->add_probe(*probe)); ASSERT_EQ(0U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); } TEST(bpftrace, add_probes_profile) { /* ast::AttachPoint a(""); a.provider = "profile"; a.target = "ms"; a.freq = 997; ast::AttachPointList attach_points = { &a }; ast::Probe probe(&attach_points, nullptr, nullptr); */ auto probe = parse_probe("profile:ms:997 {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "profile:ms:997"; check_profile(bpftrace.get_probes().at(0), "ms", 997, probe_orig_name); } TEST(bpftrace, add_probes_interval) { // ast::AttachPoint a(""); // a.provider = "interval"; // a.target = "s"; // a.freq = 1; // ast::AttachPointList attach_points = { &a }; // ast::Probe probe(&attach_points, nullptr, nullptr); auto probe = parse_probe("i:s:1 {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "interval:s:1"; check_interval(bpftrace.get_probes().at(0), "s", 1, probe_orig_name); } TEST(bpftrace, add_probes_software) { // ast::AttachPoint a(""); // a.provider = "software"; // a.target = "faults"; // a.freq = 1000; // ast::AttachPointList attach_points = { &a }; // ast::Probe probe(&attach_points, nullptr, nullptr); auto probe = parse_probe("software:faults:1000 {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "software:faults:1000"; check_software(bpftrace.get_probes().at(0), "faults", 1000, probe_orig_name); } TEST(bpftrace, add_probes_hardware) { // ast::AttachPoint a(""); // a.provider = "hardware"; // a.target = "cache-references"; // a.freq = 1000000; // ast::AttachPointList attach_points = { &a }; // ast::Probe probe(&attach_points, nullptr, nullptr); auto probe = parse_probe("hardware:cache-references:1000000 {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "hardware:cache-references:1000000"; check_hardware(bpftrace.get_probes().at(0), "cache-references", 1000000, probe_orig_name); } TEST(bpftrace, invalid_provider) { auto a = new ast::AttachPoint(""); a->provider = "lookatme"; a->func = "invalid"; auto probe = make_probe({ a }); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); } std::pair, std::vector> key_value_pair_int(std::vector key, int val) { std::pair, std::vector> pair; pair.first = std::vector(sizeof(uint64_t)*key.size()); pair.second = std::vector(sizeof(uint64_t)); uint8_t *key_data = pair.first.data(); uint8_t *val_data = pair.second.data(); for (size_t i=0; i, std::vector> key_value_pair_str(std::vector key, int val) { std::pair, std::vector> pair; pair.first = std::vector(STRING_SIZE*key.size()); pair.second = std::vector(sizeof(uint64_t)); uint8_t *key_data = pair.first.data(); uint8_t *val_data = pair.second.data(); for (size_t i=0; i, std::vector> key_value_pair_int_str(int myint, std::string mystr, int val) { std::pair, std::vector> pair; pair.first = std::vector(sizeof(uint64_t) + STRING_SIZE); pair.second = std::vector(sizeof(uint64_t)); uint8_t *key_data = pair.first.data(); uint8_t *val_data = pair.second.data(); uint64_t k = myint, v = val; std::memcpy(key_data, &k, sizeof(k)); strncpy((char*)key_data + sizeof(uint64_t), mystr.c_str(), STRING_SIZE); std::memcpy(val_data, &v, sizeof(v)); return pair; } TEST(bpftrace, sort_by_key_int) { StrictMock bpftrace; std::vector key_args = { CreateUInt64() }; std::vector, std::vector>> values_by_key = { key_value_pair_int({2}, 12), key_value_pair_int({3}, 11), key_value_pair_int({1}, 10), }; bpftrace.sort_by_key(key_args, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_int({1}, 10), key_value_pair_int({2}, 12), key_value_pair_int({3}, 11), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_int_int) { StrictMock bpftrace; std::vector key_args = { CreateUInt64(), CreateUInt64(), CreateUInt64(), }; std::vector, std::vector>> values_by_key = { key_value_pair_int({5,2,1}, 1), key_value_pair_int({5,3,1}, 2), key_value_pair_int({5,1,1}, 3), key_value_pair_int({2,2,2}, 4), key_value_pair_int({2,3,2}, 5), key_value_pair_int({2,1,2}, 6), }; bpftrace.sort_by_key(key_args, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_int({2,1,2}, 6), key_value_pair_int({2,2,2}, 4), key_value_pair_int({2,3,2}, 5), key_value_pair_int({5,1,1}, 3), key_value_pair_int({5,2,1}, 1), key_value_pair_int({5,3,1}, 2), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_str) { StrictMock bpftrace; std::vector key_args = { CreateString(STRING_SIZE), }; std::vector, std::vector>> values_by_key = { key_value_pair_str({"z"}, 1), key_value_pair_str({"a"}, 2), key_value_pair_str({"x"}, 3), key_value_pair_str({"d"}, 4), }; bpftrace.sort_by_key(key_args, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_str({"a"}, 2), key_value_pair_str({"d"}, 4), key_value_pair_str({"x"}, 3), key_value_pair_str({"z"}, 1), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_str_str) { StrictMock bpftrace; std::vector key_args = { CreateString(STRING_SIZE), CreateString(STRING_SIZE), CreateString(STRING_SIZE), }; std::vector, std::vector>> values_by_key = { key_value_pair_str({"z", "a", "l"}, 1), key_value_pair_str({"a", "a", "m"}, 2), key_value_pair_str({"z", "c", "n"}, 3), key_value_pair_str({"a", "c", "o"}, 4), key_value_pair_str({"z", "b", "p"}, 5), key_value_pair_str({"a", "b", "q"}, 6), }; bpftrace.sort_by_key(key_args, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_str({"a", "a", "m"}, 2), key_value_pair_str({"a", "b", "q"}, 6), key_value_pair_str({"a", "c", "o"}, 4), key_value_pair_str({"z", "a", "l"}, 1), key_value_pair_str({"z", "b", "p"}, 5), key_value_pair_str({"z", "c", "n"}, 3), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_int_str) { StrictMock bpftrace; std::vector key_args = { CreateUInt64(), CreateString(STRING_SIZE), }; std::vector, std::vector>> values_by_key = { key_value_pair_int_str(1, "b", 1), key_value_pair_int_str(2, "b", 2), key_value_pair_int_str(3, "b", 3), key_value_pair_int_str(1, "a", 4), key_value_pair_int_str(2, "a", 5), key_value_pair_int_str(3, "a", 6), }; bpftrace.sort_by_key(key_args, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_int_str(1, "a", 4), key_value_pair_int_str(1, "b", 1), key_value_pair_int_str(2, "a", 5), key_value_pair_int_str(2, "b", 2), key_value_pair_int_str(3, "a", 6), key_value_pair_int_str(3, "b", 3), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } #ifdef HAVE_LIBBPF_BTF_DUMP #include "btf_common.h" class bpftrace_btf : public test_btf { }; void check_probe(Probe &p, ProbeType type, const std::string &name) { EXPECT_EQ(type, p.type); EXPECT_EQ(name, p.name); } TEST_F(bpftrace_btf, add_probes_kfunc) { ast::Probe *probe = parse_probe("kfunc:func_1,kretfunc:func_1 {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(2U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_probe(bpftrace.get_probes().at(0), ProbeType::kfunc, "kfunc:func_1"); check_probe(bpftrace.get_probes().at(1), ProbeType::kretfunc, "kretfunc:func_1"); } TEST_F(bpftrace_btf, add_probes_iter_task) { ast::Probe *probe = parse_probe("iter:task {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_probe(bpftrace.get_probes().at(0), ProbeType::iter, "iter:task"); } TEST_F(bpftrace_btf, add_probes_iter_task_file) { ast::Probe *probe = parse_probe("iter:task_file {}"); StrictMock bpftrace; ASSERT_EQ(0, bpftrace.add_probe(*probe)); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_probe(bpftrace.get_probes().at(0), ProbeType::iter, "iter:task_file"); } #endif // HAVE_LIBBPF_BTF_DUMP } // namespace bpftrace } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/btf_common.h000066400000000000000000000026421413460502400170230ustar00rootroot00000000000000#pragma once #include "data/btf_data.h" class test_btf : public ::testing::Test { protected: static bool create_tmp_with_data(char *path, const unsigned char *data, unsigned int data_len) { if (!path) return false; int fd = mkstemp(path); if (fd < 0) { std::remove(path); return false; } if (write(fd, data, data_len) != data_len) { close(fd); std::remove(path); return false; } close(fd); return true; } void SetUp() override { // BTF data file char *btf_path = strdup("/tmp/btf_dataXXXXXX"); if (create_tmp_with_data(btf_path, btf_data, btf_data_len)) { setenv("BPFTRACE_BTF", btf_path, true); btf_path_ = btf_path; } // available functions file char *funcs_path = strdup("/tmp/available_filter_functionsXXXXXX"); if (create_tmp_with_data(funcs_path, func_list, func_list_len)) { setenv("BPFTRACE_AVAILABLE_FUNCTIONS_TEST", funcs_path, true); funcs_path_ = funcs_path; } } void TearDown() override { // clear the environment and remove the temp files unsetenv("BPFTRACE_BTF"); unsetenv("BPFTRACE_AVAILABLE_FUNCTIONS_TEST"); if (btf_path_) std::remove(btf_path_); if (funcs_path_) std::remove(funcs_path_); } char *btf_path_ = nullptr; char *funcs_path_ = nullptr; }; bpftrace-0.14.0/tests/child.cpp000066400000000000000000000107151413460502400163160ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include #include #include #include "child.h" #include "childhelper.h" #include "utils.h" namespace bpftrace { namespace test { namespace child { using ::testing::HasSubstr; #define TEST_BIN "/bin/ls" #define TEST_BIN_ERR "/bin/ls /does/not/exist/abc" #define TEST_BIN_SLOW "/bin/sleep 10" TEST(childproc, exe_does_not_exist) { try { ChildProc child("/does/not/exist/abc/fed"); FAIL(); } catch (const std::runtime_error &e) { EXPECT_THAT(e.what(), HasSubstr("does not exist or is not executable")); } } TEST(childproc, too_many_arguments) { std::stringstream cmd; cmd << "/bin/ls"; for (int i = 0; i < 280; i++) cmd << " a"; try { ChildProc child(cmd.str()); FAIL(); } catch (const std::runtime_error &e) { EXPECT_THAT(e.what(), HasSubstr("Too many arguments")); } } TEST(childproc, child_exit_success) { // Spawn a child that exits successfully auto child = getChild(TEST_BIN); child->run(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), 0); EXPECT_EQ(child->term_signal(), -1); } TEST(childproc, child_exit_err) { // Spawn a child that exits with an error auto child = getChild(TEST_BIN_ERR); child->run(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_TRUE(child->exit_code() > 0); EXPECT_EQ(child->term_signal(), -1); } TEST(childproc, terminate) { auto child = getChild(TEST_BIN_SLOW); child->run(); child->terminate(); wait_for(child.get(), 100); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->term_signal(), SIGTERM); } TEST(childproc, destructor_destroy_child) { pid_t child_pid = 0; { std::unique_ptr child = getChild(TEST_BIN_SLOW); child->run(); child_pid = child->pid(); // Give child a little bit of time to execve before we kill it msleep(25); } int status = 0; pid_t ret = waitpid(child_pid, &status, WNOHANG); if (ret == -1 && errno == ECHILD) return; FAIL() << "Child should've been killed but appears to be alive: ret: " << ret << ", errno: " << errno << ", status: " << status << std::endl; } TEST(childproc, child_kill_before_exec) { signal(SIGHUP, SIG_DFL); auto child = getChild(TEST_BIN_SLOW); EXPECT_EQ(kill(child->pid(), SIGHUP), 0); wait_for(child.get(), 100); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), SIGHUP); } TEST(childproc, stop_cont) { // STOP/CONT should not incorrectly mark the child // as dead auto child = getChild(TEST_BIN_SLOW); int status = 0; child->run(); msleep(25); EXPECT_TRUE(child->is_alive()); if (kill(child->pid(), SIGSTOP)) FAIL() << "kill(SIGSTOP)"; waitpid(child->pid(), &status, WUNTRACED); if (!(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP)) FAIL() << "! WIFSTOPPED"; EXPECT_TRUE(child->is_alive()); if (kill(child->pid(), SIGCONT)) FAIL() << "kill(SIGCONT)"; waitpid(child->pid(), &status, WCONTINUED); if (!WIFCONTINUED(status)) FAIL() << "! WIFCONTINUED"; EXPECT_TRUE(child->is_alive()); child->terminate(); wait_for(child.get(), 100); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), SIGTERM); } TEST(childproc, ptrace_child_exit_success) { auto child = getChild(TEST_BIN); child->run(true); child->resume(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), 0); EXPECT_EQ(child->term_signal(), -1); } TEST(childproc, ptrace_child_exit_error) { auto child = getChild(TEST_BIN_ERR); child->run(true); child->resume(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_TRUE(child->exit_code() > 0); EXPECT_EQ(child->term_signal(), -1); } TEST(childproc, ptrace_child_kill_before_execve) { auto child = getChild(TEST_BIN); child->run(true); child->terminate(true); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), 9); } TEST(childproc, ptrace_child_term_before_execve) { auto child = getChild(TEST_BIN); child->run(true); child->terminate(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), 15); } } // namespace child } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/childhelper.h000066400000000000000000000015541413460502400171640ustar00rootroot00000000000000#include #include "child.h" #include "utils.h" namespace bpftrace { namespace test { static int msleep(int msec) { struct timespec sleep = { .tv_sec = 0, .tv_nsec = msec * 1000000L }; struct timespec rem = {}; if (nanosleep(&sleep, &rem) < 0) return 1000L * rem.tv_sec + 1000000L * rem.tv_nsec; return 0; } static void wait_for(ChildProcBase *child, int msec_timeout) { constexpr int wait = 10; while (child->is_alive() && msec_timeout > 0) msec_timeout -= wait - msleep(wait); } static std::unique_ptr getChild(std::string cmd) { std::unique_ptr child; { StderrSilencer es; StdoutSilencer os; os.silence(); es.silence(); child = std::make_unique(cmd); } EXPECT_NE(child->pid(), -1); EXPECT_TRUE(child->is_alive()); return child; } } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/clang_parser.cpp000066400000000000000000000637731413460502400177070ustar00rootroot00000000000000#include "clang_parser.h" #include "ast/passes/field_analyser.h" #include "bpftrace.h" #include "driver.h" #include "struct.h" #include "gtest/gtest.h" #include namespace bpftrace { namespace test { namespace clang_parser { static void parse(const std::string &input, BPFtrace &bpftrace, bool result = true, const std::string& probe = "kprobe:sys_read { 1 }") { auto extended_input = input + probe; Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(extended_input), 0); ast::FieldAnalyser fields(driver.root_, bpftrace); EXPECT_EQ(fields.analyse(), 0); ClangParser clang; ASSERT_EQ(clang.parse(driver.root_, bpftrace), result); } TEST(clang_parser, integers) { BPFtrace bpftrace; parse("struct Foo { int x; int y, z; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 12); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_EQ(foo->HasField("x"), true); ASSERT_EQ(foo->HasField("y"), true); ASSERT_EQ(foo->HasField("z"), true); EXPECT_EQ(foo->GetField("x").type.type, Type::integer); EXPECT_EQ(foo->GetField("x").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_EQ(foo->GetField("y").type.type, Type::integer); EXPECT_EQ(foo->GetField("y").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("y").offset, 4); EXPECT_EQ(foo->GetField("z").type.type, Type::integer); EXPECT_EQ(foo->GetField("z").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("z").offset, 8); } TEST(clang_parser, c_union) { BPFtrace bpftrace; parse("union Foo { char c; short s; int i; long l; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("union Foo")); auto foo = bpftrace.structs.Lookup("union Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 4U); ASSERT_TRUE(foo->HasField("c")); ASSERT_TRUE(foo->HasField("s")); ASSERT_TRUE(foo->HasField("i")); ASSERT_TRUE(foo->HasField("l")); EXPECT_EQ(foo->GetField("c").type.type, Type::integer); EXPECT_EQ(foo->GetField("c").type.GetSize(), 1U); EXPECT_EQ(foo->GetField("c").offset, 0); EXPECT_EQ(foo->GetField("s").type.type, Type::integer); EXPECT_EQ(foo->GetField("s").type.GetSize(), 2U); EXPECT_EQ(foo->GetField("s").offset, 0); EXPECT_EQ(foo->GetField("i").type.type, Type::integer); EXPECT_EQ(foo->GetField("i").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("i").offset, 0); EXPECT_EQ(foo->GetField("l").type.type, Type::integer); EXPECT_EQ(foo->GetField("l").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("l").offset, 0); } TEST(clang_parser, c_enum) { BPFtrace bpftrace; parse("enum E {NONE}; struct Foo { enum E e; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("e")); EXPECT_EQ(foo->GetField("e").type.type, Type::integer); EXPECT_EQ(foo->GetField("e").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("e").offset, 0); } TEST(clang_parser, integer_ptr) { BPFtrace bpftrace; parse("struct Foo { int *x; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("x")); EXPECT_TRUE(foo->GetField("x").type.IsPtrTy()); EXPECT_EQ(foo->GetField("x").type.GetPointeeTy()->GetIntBitWidth(), 8 * sizeof(int)); EXPECT_EQ(foo->GetField("x").offset, 0); } TEST(clang_parser, string_ptr) { BPFtrace bpftrace; parse("struct Foo { char *str; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("str")); auto &ty = foo->GetField("str").type; auto *pointee = ty.GetPointeeTy(); EXPECT_TRUE(pointee->IsIntTy()); EXPECT_EQ(pointee->GetIntBitWidth(), 8 * sizeof(char)); EXPECT_EQ(foo->GetField("str").offset, 0); } TEST(clang_parser, string_array) { BPFtrace bpftrace; parse("struct Foo { char str[32]; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 32); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("str")); EXPECT_EQ(foo->GetField("str").type.type, Type::string); EXPECT_EQ(foo->GetField("str").type.GetSize(), 32U); EXPECT_EQ(foo->GetField("str").offset, 0); } TEST(clang_parser, nested_struct_named) { BPFtrace bpftrace; parse("struct Bar { int x; } struct Foo { struct Bar bar; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has("struct Bar")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("bar")); auto &bar = foo->GetField("bar"); EXPECT_TRUE(bar.type.IsRecordTy()); EXPECT_EQ(bar.type.GetName(), "struct Bar"); EXPECT_EQ(bar.type.GetSize(), 4U); EXPECT_EQ(bar.offset, 0); } TEST(clang_parser, nested_struct_ptr_named) { BPFtrace bpftrace; parse("struct Bar { int x; } struct Foo { struct Bar *bar; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has("struct Bar")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("bar")); auto &bar = foo->GetField("bar"); EXPECT_TRUE(bar.type.IsPtrTy()); EXPECT_TRUE(bar.type.GetPointeeTy()->IsRecordTy()); EXPECT_EQ(bar.type.GetPointeeTy()->GetName(), "struct Bar"); EXPECT_EQ(bar.type.GetPointeeTy()->GetSize(), sizeof(int)); EXPECT_EQ(bar.offset, 0); } TEST(clang_parser, nested_struct_no_type) { BPFtrace bpftrace; // bar and baz's struct/union do not have type names, but are not anonymous // since they are called bar and baz parse("struct Foo { struct { int x; } bar; union { int y; } baz; }", bpftrace); std::string bar_name = "struct Foo::(anonymous at definitions.h:2:14)"; std::string baz_name = "union Foo::(anonymous at definitions.h:2:37)"; ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has(bar_name)); ASSERT_TRUE(bpftrace.structs.Has(baz_name)); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); auto bar = bpftrace.structs.Lookup(bar_name).lock(); auto baz = bpftrace.structs.Lookup(baz_name).lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 2U); ASSERT_TRUE(foo->HasField("bar")); ASSERT_TRUE(foo->HasField("baz")); EXPECT_EQ(bar->size, 4); ASSERT_EQ(bar->fields.size(), 1U); ASSERT_TRUE(bar->HasField("x")); EXPECT_EQ(bar->GetField("x").type.type, Type::integer); EXPECT_EQ(bar->GetField("x").type.GetSize(), 4U); EXPECT_EQ(bar->GetField("x").offset, 0); EXPECT_EQ(baz->size, 4); ASSERT_EQ(baz->fields.size(), 1U); ASSERT_TRUE(baz->HasField("y")); EXPECT_EQ(baz->GetField("y").type.type, Type::integer); EXPECT_EQ(baz->GetField("y").type.GetSize(), 4U); EXPECT_EQ(baz->GetField("y").offset, 0); { auto &bar_field = foo->GetField("bar"); EXPECT_TRUE(bar_field.type.IsRecordTy()); EXPECT_EQ(bar_field.type.GetSize(), sizeof(int)); EXPECT_EQ(bar_field.offset, 0); auto &baz_field = foo->GetField("baz"); EXPECT_TRUE(baz_field.type.IsRecordTy()); EXPECT_EQ(baz_field.type.GetSize(), sizeof(int)); EXPECT_EQ(baz_field.offset, 4); } } TEST(clang_parser, nested_struct_unnamed_fields) { BPFtrace bpftrace; parse("struct Foo" "{" " struct { int x; int y; };" // Anonymous struct field " int a;" " struct Bar { int z; };" // Struct definition - not a field of Foo "}", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has("struct Bar")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); auto bar = bpftrace.structs.Lookup("struct Bar").lock(); EXPECT_EQ(foo->size, 12); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_TRUE(foo->HasField("x")); ASSERT_TRUE(foo->HasField("y")); ASSERT_TRUE(foo->HasField("a")); EXPECT_EQ(foo->GetField("x").type.type, Type::integer); EXPECT_EQ(foo->GetField("x").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_EQ(foo->GetField("y").type.type, Type::integer); EXPECT_EQ(foo->GetField("y").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("y").offset, 4); EXPECT_EQ(foo->GetField("a").type.type, Type::integer); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 8); EXPECT_EQ(bar->size, 4); EXPECT_EQ(bar->fields.size(), 1U); EXPECT_TRUE(bar->HasField("z")); EXPECT_EQ(bar->GetField("z").type.type, Type::integer); EXPECT_EQ(bar->GetField("z").type.GetSize(), 4U); EXPECT_EQ(bar->GetField("z").offset, 0); } TEST(clang_parser, nested_struct_anon_union_struct) { BPFtrace bpftrace; parse("struct Foo" "{" " union" " {" " long long _xy;" " struct { int x; int y;};" " };" " int a;" " struct { int z; };" "}", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 16); ASSERT_EQ(foo->fields.size(), 5U); ASSERT_TRUE(foo->HasField("_xy")); ASSERT_TRUE(foo->HasField("x")); ASSERT_TRUE(foo->HasField("y")); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("z")); EXPECT_EQ(foo->GetField("_xy").type.type, Type::integer); EXPECT_EQ(foo->GetField("_xy").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("_xy").offset, 0); EXPECT_EQ(foo->GetField("x").type.type, Type::integer); EXPECT_EQ(foo->GetField("x").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_EQ(foo->GetField("y").type.type, Type::integer); EXPECT_EQ(foo->GetField("y").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("y").offset, 4); EXPECT_EQ(foo->GetField("a").type.type, Type::integer); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 8); EXPECT_EQ(foo->GetField("z").type.type, Type::integer); EXPECT_EQ(foo->GetField("z").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("z").offset, 12); } TEST(clang_parser, bitfields) { BPFtrace bpftrace; parse("struct Foo { int a:8, b:8, c:16; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("b")); ASSERT_TRUE(foo->HasField("c")); EXPECT_EQ(foo->GetField("a").type.type, Type::integer); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 0); EXPECT_TRUE(foo->GetField("a").is_bitfield); EXPECT_EQ(foo->GetField("a").bitfield.read_bytes, 0x1U); EXPECT_EQ(foo->GetField("a").bitfield.access_rshift, 0U); EXPECT_EQ(foo->GetField("a").bitfield.mask, 0xFFU); EXPECT_EQ(foo->GetField("b").type.type, Type::integer); EXPECT_EQ(foo->GetField("b").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("b").offset, 1); EXPECT_TRUE(foo->GetField("b").is_bitfield); EXPECT_EQ(foo->GetField("b").bitfield.read_bytes, 0x1U); EXPECT_EQ(foo->GetField("b").bitfield.access_rshift, 0U); EXPECT_EQ(foo->GetField("b").bitfield.mask, 0xFFU); EXPECT_EQ(foo->GetField("c").type.type, Type::integer); EXPECT_EQ(foo->GetField("c").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("c").offset, 2); EXPECT_TRUE(foo->GetField("c").is_bitfield); EXPECT_EQ(foo->GetField("c").bitfield.read_bytes, 0x2U); EXPECT_EQ(foo->GetField("c").bitfield.access_rshift, 0U); EXPECT_EQ(foo->GetField("c").bitfield.mask, 0xFFFFU); } TEST(clang_parser, bitfields_uneven_fields) { BPFtrace bpftrace; parse("struct Foo { int a:1, b:1, c:3, d:20, e:7; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 5U); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("b")); ASSERT_TRUE(foo->HasField("c")); ASSERT_TRUE(foo->HasField("d")); ASSERT_TRUE(foo->HasField("e")); EXPECT_EQ(foo->GetField("a").type.type, Type::integer); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 0); EXPECT_TRUE(foo->GetField("a").is_bitfield); EXPECT_EQ(foo->GetField("a").bitfield.read_bytes, 1U); EXPECT_EQ(foo->GetField("a").bitfield.access_rshift, 0U); EXPECT_EQ(foo->GetField("a").bitfield.mask, 0x1U); EXPECT_EQ(foo->GetField("b").type.type, Type::integer); EXPECT_EQ(foo->GetField("b").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("b").offset, 0); EXPECT_TRUE(foo->GetField("b").is_bitfield); EXPECT_EQ(foo->GetField("b").bitfield.read_bytes, 1U); EXPECT_EQ(foo->GetField("b").bitfield.access_rshift, 1U); EXPECT_EQ(foo->GetField("b").bitfield.mask, 0x1U); EXPECT_EQ(foo->GetField("c").type.type, Type::integer); EXPECT_EQ(foo->GetField("c").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("c").offset, 0); EXPECT_TRUE(foo->GetField("c").is_bitfield); EXPECT_EQ(foo->GetField("c").bitfield.read_bytes, 1U); EXPECT_EQ(foo->GetField("c").bitfield.access_rshift, 2U); EXPECT_EQ(foo->GetField("c").bitfield.mask, 0x7U); EXPECT_EQ(foo->GetField("d").type.type, Type::integer); EXPECT_EQ(foo->GetField("d").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("d").offset, 0); EXPECT_TRUE(foo->GetField("d").is_bitfield); EXPECT_EQ(foo->GetField("d").bitfield.read_bytes, 4U); EXPECT_EQ(foo->GetField("d").bitfield.access_rshift, 5U); EXPECT_EQ(foo->GetField("d").bitfield.mask, 0xFFFFFU); EXPECT_EQ(foo->GetField("e").type.type, Type::integer); EXPECT_EQ(foo->GetField("e").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("e").offset, 3); EXPECT_TRUE(foo->GetField("e").is_bitfield); EXPECT_EQ(foo->GetField("e").bitfield.read_bytes, 1U); EXPECT_EQ(foo->GetField("e").bitfield.access_rshift, 1U); EXPECT_EQ(foo->GetField("e").bitfield.mask, 0x7FU); } TEST(clang_parser, bitfields_with_padding) { BPFtrace bpftrace; parse("struct Foo { int pad; int a:28, b:4; long int end;}", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 16); ASSERT_EQ(foo->fields.size(), 4U); ASSERT_TRUE(foo->HasField("pad")); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("b")); ASSERT_TRUE(foo->HasField("end")); EXPECT_EQ(foo->GetField("a").type.type, Type::integer); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 4); EXPECT_TRUE(foo->GetField("a").is_bitfield); EXPECT_EQ(foo->GetField("a").bitfield.read_bytes, 4U); EXPECT_EQ(foo->GetField("a").bitfield.access_rshift, 0U); EXPECT_EQ(foo->GetField("a").bitfield.mask, 0xFFFFFFFU); EXPECT_EQ(foo->GetField("b").type.type, Type::integer); EXPECT_EQ(foo->GetField("b").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("b").offset, 7); EXPECT_TRUE(foo->GetField("b").is_bitfield); EXPECT_EQ(foo->GetField("b").bitfield.read_bytes, 1U); EXPECT_EQ(foo->GetField("b").bitfield.access_rshift, 4U); EXPECT_EQ(foo->GetField("b").bitfield.mask, 0xFU); } TEST(clang_parser, builtin_headers) { // size_t is definied in stddef.h BPFtrace bpftrace; parse("#include \nstruct Foo { size_t x, y, z; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 24); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_TRUE(foo->HasField("x")); ASSERT_TRUE(foo->HasField("y")); ASSERT_TRUE(foo->HasField("z")); EXPECT_EQ(foo->GetField("x").type.type, Type::integer); EXPECT_EQ(foo->GetField("x").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_EQ(foo->GetField("y").type.type, Type::integer); EXPECT_EQ(foo->GetField("y").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("y").offset, 8); EXPECT_EQ(foo->GetField("z").type.type, Type::integer); EXPECT_EQ(foo->GetField("z").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("z").offset, 16); } TEST(clang_parser, macro_preprocessor) { BPFtrace bpftrace; parse("#define FOO size_t\n k:f { 0 }", bpftrace); parse("#define _UNDERSCORE 314\n k:f { 0 }", bpftrace); auto ¯os = bpftrace.macros_; ASSERT_EQ(macros.count("FOO"), 1U); EXPECT_EQ(macros["FOO"], "size_t"); ASSERT_EQ(macros.count("_UNDERSCORE"), 1U); EXPECT_EQ(macros["_UNDERSCORE"], "314"); } TEST(clang_parser, parse_fail) { BPFtrace bpftrace; parse("struct a { int a; struct b b; };", bpftrace, false); } #ifdef HAVE_LIBBPF_BTF_DUMP #include "btf_common.h" class clang_parser_btf : public test_btf { }; TEST_F(clang_parser_btf, btf) { BPFtrace bpftrace; parse("", bpftrace, true, "kprobe:sys_read {\n" " @x1 = (struct Foo1 *) curtask;\n" " @x2 = (struct Foo2 *) curtask;\n" " @x3 = (struct Foo3 *) curtask;\n" "}"); ASSERT_TRUE(bpftrace.structs.Has("struct Foo1")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo2")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo3")); ASSERT_TRUE(bpftrace.structs.Has("struct task_struct")); auto foo1 = bpftrace.structs.Lookup("struct Foo1").lock(); auto foo2 = bpftrace.structs.Lookup("struct Foo2").lock(); auto foo3 = bpftrace.structs.Lookup("struct Foo3").lock(); auto task_struct = bpftrace.structs.Lookup("struct task_struct").lock(); EXPECT_EQ(foo1->size, 16); ASSERT_EQ(foo1->fields.size(), 3U); ASSERT_TRUE(foo1->HasField("a")); ASSERT_TRUE(foo1->HasField("b")); ASSERT_TRUE(foo1->HasField("c")); EXPECT_EQ(foo1->GetField("a").type.type, Type::integer); EXPECT_EQ(foo1->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo1->GetField("a").offset, 0); EXPECT_EQ(foo1->GetField("b").type.type, Type::integer); EXPECT_EQ(foo1->GetField("b").type.GetSize(), 1U); EXPECT_EQ(foo1->GetField("b").offset, 4); EXPECT_EQ(foo1->GetField("c").type.type, Type::integer); EXPECT_EQ(foo1->GetField("c").type.GetSize(), 8U); EXPECT_EQ(foo1->GetField("c").offset, 8); EXPECT_EQ(foo2->size, 24); ASSERT_EQ(foo2->fields.size(), 3U); ASSERT_TRUE(foo2->HasField("a")); ASSERT_TRUE(foo2->HasField("f")); ASSERT_TRUE(foo2->HasField("g")); EXPECT_EQ(foo2->GetField("a").type.type, Type::integer); EXPECT_EQ(foo2->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo2->GetField("a").offset, 0); EXPECT_EQ(foo2->GetField("f").type.type, Type::record); EXPECT_EQ(foo2->GetField("f").type.GetSize(), 16U); EXPECT_EQ(foo2->GetField("f").offset, 8); EXPECT_EQ(foo2->GetField("g").type.type, Type::integer); EXPECT_EQ(foo2->GetField("g").type.GetSize(), 1U); EXPECT_EQ(foo2->GetField("g").offset, 8); EXPECT_EQ(foo3->size, 16); ASSERT_EQ(foo3->fields.size(), 2U); ASSERT_TRUE(foo3->HasField("foo1")); ASSERT_TRUE(foo3->HasField("foo2")); EXPECT_EQ(task_struct->size, 8); ASSERT_EQ(task_struct->fields.size(), 2U); ASSERT_TRUE(task_struct->HasField("pid")); ASSERT_TRUE(task_struct->HasField("pgid")); auto foo1_field = foo3->GetField("foo1"); auto foo2_field = foo3->GetField("foo2"); EXPECT_TRUE(foo1_field.type.IsPtrTy()); EXPECT_EQ(foo1_field.type.GetPointeeTy()->GetName(), "struct Foo1"); EXPECT_EQ(foo1_field.offset, 0); EXPECT_TRUE(foo2_field.type.IsPtrTy()); EXPECT_EQ(foo2_field.type.GetPointeeTy()->GetName(), "struct Foo2"); EXPECT_EQ(foo2_field.offset, 8); } TEST_F(clang_parser_btf, btf_field_struct) { BPFtrace bpftrace; parse("", bpftrace, true, "kprobe:sys_read {\n" " @x3 = ((struct Foo3 *) curtask)->foo2->g;\n" "}"); /* task_struct->Foo3->Foo2->char */ EXPECT_EQ(bpftrace.btf_set_.size(), 4U); EXPECT_NE(bpftrace.btf_set_.find("struct task_struct"), bpftrace.btf_set_.end()); EXPECT_NE(bpftrace.btf_set_.find("struct Foo3"), bpftrace.btf_set_.end()); EXPECT_NE(bpftrace.btf_set_.find("struct Foo2"), bpftrace.btf_set_.end()); EXPECT_NE(bpftrace.btf_set_.find("char"), bpftrace.btf_set_.end()); } TEST_F(clang_parser_btf, btf_variable_field_struct) { BPFtrace bpftrace; parse("", bpftrace, true, "kprobe:sys_read {\n" " @x1 = ((struct Foo3 *) curtask);\n" " @x2 = ((struct Foo1 *) curtask);\n" " @x3 = @x1->foo2;\n" "}"); EXPECT_EQ(bpftrace.btf_set_.size(), 4U); EXPECT_NE(bpftrace.btf_set_.find("struct task_struct"), bpftrace.btf_set_.end()); EXPECT_NE(bpftrace.btf_set_.find("struct Foo1"), bpftrace.btf_set_.end()); // struct Foo2 should be added by @x1->foo2 EXPECT_NE(bpftrace.btf_set_.find("struct Foo2"), bpftrace.btf_set_.end()); EXPECT_NE(bpftrace.btf_set_.find("struct Foo3"), bpftrace.btf_set_.end()); } TEST(clang_parser, btf_unresolved_typedef) { // size_t is defined in stddef.h, but if we have BTF, it should be possible to // extract it from there BPFtrace bpftrace; if (!bpftrace.btf_.has_data()) GTEST_SKIP(); parse("struct Foo { size_t x; };", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("x")); EXPECT_EQ(foo->GetField("x").type.type, Type::integer); EXPECT_EQ(foo->GetField("x").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("x").offset, 0); } TEST_F(clang_parser_btf, btf_type_override) { // It should be possible to override types from BTF, ... BPFtrace bpftrace; parse("struct Foo1 { int a; };\n", bpftrace, true, "kprobe:sys_read { @x = ((struct Foo1 *)curtask); }"); ASSERT_TRUE(bpftrace.structs.Has("struct Foo1")); auto foo1 = bpftrace.structs.Lookup("struct Foo1").lock(); ASSERT_EQ(foo1->fields.size(), 1U); ASSERT_TRUE(foo1->HasField("a")); // ... however, in such case, no other types are taken from BTF and the // following will fail since Foo2 will be undefined bpftrace.btf_set_.clear(); parse("struct Foo1 { struct Foo2 foo2; };\n", bpftrace, false, "kprobe:sys_read { @x = ((struct Foo1 *)curtask); }"); // Here, Foo1 redefinition will take place when resolving incomplete types // (since Foo3 contains a pointer to Foo1) bpftrace.btf_set_.clear(); parse("struct Foo1 { struct Foo2 foo2; };\n", bpftrace, false, "kprobe:sys_read { @x1 = ((struct Foo3 *)curtask); }"); } #endif // HAVE_LIBBPF_BTF_DUMP TEST(clang_parser, struct_typedef) { // Make sure we can differentiate between "struct max_align_t {}" and // "typedef struct {} max_align_t" BPFtrace bpftrace; parse("#include <__stddef_max_align_t.h>\n" "struct max_align_t { int x; };", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct max_align_t")); ASSERT_TRUE(bpftrace.structs.Has("max_align_t")); auto max_align_struct = bpftrace.structs.Lookup("struct max_align_t").lock(); auto max_align_typedef = bpftrace.structs.Lookup("max_align_t").lock(); // Non-typedef'd struct EXPECT_EQ(max_align_struct->size, 4); ASSERT_EQ(max_align_struct->fields.size(), 1U); ASSERT_TRUE(max_align_struct->HasField("x")); EXPECT_EQ(max_align_struct->GetField("x").type.type, Type::integer); EXPECT_EQ(max_align_struct->GetField("x").type.GetSize(), 4U); EXPECT_EQ(max_align_struct->GetField("x").offset, 0); // typedef'd struct (defined in __stddef_max_align_t.h builtin header) EXPECT_EQ(max_align_typedef->size, 32); ASSERT_EQ(max_align_typedef->fields.size(), 2U); ASSERT_TRUE(max_align_typedef->HasField("__clang_max_align_nonce1")); ASSERT_TRUE(max_align_typedef->HasField("__clang_max_align_nonce2")); EXPECT_EQ(max_align_typedef->GetField("__clang_max_align_nonce1").type.type, Type::integer); EXPECT_EQ( max_align_typedef->GetField("__clang_max_align_nonce1").type.GetSize(), 8U); EXPECT_EQ(max_align_typedef->GetField("__clang_max_align_nonce1").offset, 0); // double are not parsed correctly yet so these fields are junk for now EXPECT_EQ(max_align_typedef->GetField("__clang_max_align_nonce2").type.type, Type::none); EXPECT_EQ( max_align_typedef->GetField("__clang_max_align_nonce2").type.GetSize(), 0U); EXPECT_EQ(max_align_typedef->GetField("__clang_max_align_nonce2").offset, 16); } TEST(clang_parser, struct_qualifiers) { BPFtrace bpftrace; parse("struct a {int a} struct b { volatile const struct a* restrict a; " "const struct a a2; };", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct b")); auto SB = bpftrace.structs.Lookup("struct b").lock(); EXPECT_EQ(SB->size, 16); EXPECT_EQ(SB->fields.size(), 2U); EXPECT_TRUE(SB->GetField("a").type.IsPtrTy()); EXPECT_TRUE(SB->GetField("a").type.GetPointeeTy()->IsRecordTy()); EXPECT_EQ(SB->GetField("a").type.GetPointeeTy()->GetName(), "struct a"); EXPECT_TRUE(SB->GetField("a2").type.IsRecordTy()); EXPECT_EQ(SB->GetField("a2").type.GetName(), "struct a"); } TEST(clang_parser, redefined_types) { BPFtrace bpftrace; parse("struct a {int a;}; struct a {int a;};", bpftrace, false); parse("struct a {int a;}; struct a {int a; short b;};", bpftrace, false); } TEST(clang_parser, data_loc_annotation) { BPFtrace bpftrace; std::string input = R"_( struct _tracepoint_irq_irq_handler_entry { int common_pid; int irq; __attribute__((annotate("tp_data_loc"))) char * name; }; )_"; parse(input, bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct _tracepoint_irq_irq_handler_entry")); auto s = bpftrace.structs.Lookup("struct _tracepoint_irq_irq_handler_entry") .lock(); EXPECT_EQ(s->size, 16); EXPECT_EQ(s->fields.size(), 3U); EXPECT_TRUE(s->GetField("common_pid").type.IsIntTy()); EXPECT_TRUE(s->GetField("irq").type.IsIntTy()); // The parser needs to rewrite __data_loc fields to be u64 so it can hold // a pointer to the actual data. The kernel tracepoint infra exports an // encoded u32 which codegen will know how to decode. EXPECT_TRUE(s->GetField("name").is_data_loc); ASSERT_TRUE(s->GetField("name").type.IsIntTy()); EXPECT_EQ(s->GetField("name").type.GetIntBitWidth(), 64ULL); } } // namespace clang_parser } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen-validator.sh000077500000000000000000000011001413460502400204410ustar00rootroot00000000000000#!/bin/bash ## Don't add to this EXIT=0 LLVM_VERSION=12 if ! LLVM=$(command -v "llvm-as-${LLVM_VERSION}" || command -v llvm-as); then echo "llvm-as not found, exiting" exit 1 fi if ! $LLVM --version | grep -q "LLVM version ${LLVM_VERSION}"; then echo "llvm-as is not version ${LLVM_VERSION}" fi if [[ -z "$1" ]]; then echo "Usage: $0 " exit 1 fi for file in "${1}"/tests/codegen/llvm/*.ll; do if $LLVM -o /dev/null "${file}"; then echo -e "[ OK ]\t$file" else echo -e "[ FAILED ]\t$file" EXIT=1 fi done exit $EXIT bpftrace-0.14.0/tests/codegen/000077500000000000000000000000001413460502400161275ustar00rootroot00000000000000bpftrace-0.14.0/tests/codegen/args_multiple_tracepoints.cpp000066400000000000000000000006741413460502400241240ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, args_multiple_tracepoints) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "tracepoint:sched:sched_one,tracepoint:sched:sched_two { " "@[args->common_field] = count(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/args_multiple_tracepoints_category_wild.cpp000066400000000000000000000006441413460502400270350ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, args_multiple_tracepoints_category_wild) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "tracepoint:sched*:sched_* { @[args->common_field] = count(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/args_multiple_tracepoints_wild.cpp000066400000000000000000000006331413460502400251360ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, args_multiple_tracepoints_wild) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "tracepoint:sched:sched_* { @[args->common_field] = count(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/basic_while_loop.cpp000066400000000000000000000003701413460502400221350ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, basic_while_loop) { test("i:s:1 { $a = 1; while ($a <= 150) { @=$a++; }}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/bitshift_left.cpp000066400000000000000000000003511413460502400214600ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, bitshift_left) { test("kprobe:f { @x = 1 << 10; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/bitshift_right.cpp000066400000000000000000000003541413460502400216460ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, bitshift_right) { test("kprobe:f { @x = 1024 >> 9; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/bitwise_not.cpp000066400000000000000000000003301413460502400211550ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, bitwise_not) { test("BEGIN { @x = ~10; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_arg.cpp000066400000000000000000000003561413460502400211360ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_arg) { test("kprobe:f { @x = arg0; @y = arg2 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_comm.cpp000066400000000000000000000003341413460502400213140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_comm) { test("kprobe:f { @x = comm }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_cpid.cpp000066400000000000000000000004671413460502400213070ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_cpid) { MockBPFtrace bpftrace; bpftrace.child_ = std::make_unique(""); test(bpftrace, "kprobe:f { @ = cpid }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_cpu.cpp000066400000000000000000000003421413460502400211470ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_cpu) { test("kprobe:f { @x = cpu }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_ctx.cpp000066400000000000000000000003421413460502400211560ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_ctx) { test("kprobe:f { @x = (uint64)ctx }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_ctx_field.cpp000066400000000000000000000007141413460502400223240ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_ctx_field) { std::string prog = R"END( struct c { char c; }; struct x { long a; short b[4]; struct c c; struct c *d; char e[4] }; kprobe:f { $x = (struct x*)ctx; @a = $x->a; @b = $x->b[0]; @c = $x->c.c; @d = $x->d->c; @e = $x->e; } )END"; test(prog, NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_curtask.cpp000066400000000000000000000003521413460502400220350ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_curtask) { test("kprobe:f { @x = curtask }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_elapsed.cpp000066400000000000000000000003471413460502400220020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_elapsed) { test("i:s:1 { @ = elapsed; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_func.cpp000066400000000000000000000005461413460502400213210ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_func) { test("kprobe:f { @x = func }", NAME); } TEST(codegen, builtin_func_uprobe) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "uprobe:/bin/sh:f { @x = func }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_func_wild.cpp000066400000000000000000000005341413460502400223350ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::Return; TEST(codegen, builtin_func_wild) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "kprobe:do_execve* { @x = func }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_kstack.cpp000066400000000000000000000003501413460502400216370ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_kstack) { test("kprobe:f { @x = kstack }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_nsecs.cpp000066400000000000000000000003461413460502400214770ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_nsecs) { test("kprobe:f { @x = nsecs }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_pid_tid.cpp000066400000000000000000000003601413460502400217740ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_pid_tid) { test("kprobe:f { @x = pid; @y = tid }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_probe.cpp000066400000000000000000000005101413460502400214640ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_probe) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "tracepoint:sched:sched_one { @x = probe }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_probe_wild.cpp000066400000000000000000000005471413460502400225150ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::Return; TEST(codegen, builtin_probe_wild) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "tracepoint:sched:sched_on* { @x = probe }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_rand.cpp000066400000000000000000000003441413460502400213060ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_rand) { test("kprobe:f { @x = rand }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_retval.cpp000066400000000000000000000003531413460502400216570ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_retval) { test("kretprobe:f { @x = retval }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_sarg.cpp000066400000000000000000000003611413460502400213150ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_sarg) { test("kprobe:f { @x = sarg0; @y = sarg2 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_uid_gid.cpp000066400000000000000000000003601413460502400217640ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_uid_gid) { test("kprobe:f { @x = uid; @y = gid }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_username.cpp000066400000000000000000000003651413460502400222040ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_username) { test("kprobe:f { @x = username; @y = gid}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/builtin_ustack.cpp000066400000000000000000000003501413460502400216510ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_ustack) { test("kprobe:f { @x = ustack }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_avg.cpp000066400000000000000000000003341413460502400204030ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_avg) { test("kprobe:f { @x = avg(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_buf.cpp000066400000000000000000000007671413460502400204140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_buf_implicit_size) { test("struct x { int c[4] }; kprobe:f { $foo = (struct x*)0; @x = " "buf($foo->c); }", NAME); } TEST(codegen, call_buf_size_literal) { test("kprobe:f { @x = buf(arg0, 1) }", NAME); } TEST(codegen, call_buf_size_nonliteral) { test("kprobe:f { @x = buf(arg0, arg1) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_cat.cpp000066400000000000000000000003561413460502400204010ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_cat) { test("kprobe:f { cat(\"/proc/loadavg\"); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_cgroup.cpp000066400000000000000000000004431413460502400211260ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_cgroup) { test("tracepoint:syscalls:sys_enter_openat /cgroup == 0x100000001/ { @x = " "cgroup }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_clear.cpp000066400000000000000000000003651413460502400207200ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_clear) { test("BEGIN { @x = 1; } kprobe:f { clear(@x); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_count.cpp000066400000000000000000000003451413460502400207600ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_count) { test("kprobe:f { @x = count() }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_delete.cpp000066400000000000000000000003541413460502400210720ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_delete) { test("kprobe:f { @x = 1; delete(@x) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_exit.cpp000066400000000000000000000003341413460502400205770ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_exit) { test("kprobe:f { exit(); @=10 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_hist.cpp000066400000000000000000000003461413460502400206000ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_hist) { test("kprobe:f { @x = hist(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_kstack.cpp000066400000000000000000000052161413460502400211120ustar00rootroot00000000000000#include "common.h" #include namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_kstack) { auto result = NAME; // Mode doesn't directly affect codegen, so the programs below should // generate the same IR test("kprobe:f { @x = kstack(); @y = kstack(6) }", result); test("kprobe:f { @x = kstack(perf); @y = kstack(perf, 6) }", result); test("kprobe:f { @x = kstack(perf); @y = kstack(bpftrace) }", result); } TEST(codegen, call_kstack_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "kprobe:f { @x = kstack(5); @y = kstack(6); @z = kstack(6) }"), 0); ClangParser clang; clang.parse(driver.root_, *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; ast::CodegenLLVM codegen(driver.root_, *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); ASSERT_EQ(bpftrace->maps.CountStackTypes(), 2U); StackType stack_type; stack_type.limit = 5; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); stack_type.limit = 6; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); } TEST(codegen, call_kstack_modes_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str("kprobe:f { @x = kstack(perf); @y = " "kstack(bpftrace); @z = kstack() }"), 0); ClangParser clang; clang.parse(driver.root_, *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; ast::CodegenLLVM codegen(driver.root_, *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); ASSERT_EQ(bpftrace->maps.CountStackTypes(), 2U); StackType stack_type; stack_type.mode = StackMode::perf; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); stack_type.mode = StackMode::bpftrace; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_lhist.cpp000066400000000000000000000003631413460502400207530ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_lhist) { test("kprobe:f { @x = lhist(pid, 0, 100, 1) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_macaddr.cpp000066400000000000000000000004571413460502400212270ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_macaddr) { test("struct mac { unsigned char addr[6] } kprobe:f { @x = macaddr(((struct " "mac*)0)->addr); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_max.cpp000066400000000000000000000003441413460502400204140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_max) { test("kprobe:f { @x = max(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_min.cpp000066400000000000000000000003441413460502400204120ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_min) { test("kprobe:f { @x = min(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_ntop_char16.cpp000066400000000000000000000004631413460502400217550ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ntop_char16) { test("struct inet { unsigned char addr[16] } kprobe:f { @x = ntop(((struct " "inet*)0)->addr); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_ntop_char4.cpp000066400000000000000000000004611413460502400216700ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ntop_char4) { test("struct inet { unsigned char addr[4] } kprobe:f { @x = ntop(((struct " "inet*)0)->addr); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_ntop_key.cpp000066400000000000000000000003741413460502400214620ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ntop_key) { test("kprobe:f { @x[ntop(2, 0xFFFFFFFF)] = count()}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_override.cpp000066400000000000000000000003521413460502400214450ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_override) { test("kprobe:f { override(arg0); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_override_literal.cpp000066400000000000000000000003601413460502400231600ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_override_literal) { test("kprobe:f { override(-1); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_print.cpp000066400000000000000000000003651413460502400207660ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_print) { test("BEGIN { @x = 1; } kprobe:f { print(@x); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_print_non_map.cpp000066400000000000000000000004731413460502400224750ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_print_int) { test("k:f { print(3) }", NAME); } TEST(codegen, call_print_composit) { test("k:f { print((1,\"abc\")) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_printf.cpp000066400000000000000000000005041413460502400211270ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_printf) { test("struct Foo { char c; long l; } kprobe:f { $foo = (struct Foo*)arg0; " "printf(\"%c %lu\\n\", $foo->c, $foo->l) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_reg.cpp000066400000000000000000000004701413460502400204040ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { #ifdef ARCH_X86_64 TEST(codegen, call_reg) // Identical to builtin_func apart from variable names { test("kprobe:f { @x = reg(\"ip\") }", NAME); } #endif } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_signal.cpp000066400000000000000000000003411413460502400211010ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_signal) { test("k:f { signal(arg0); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_signal_literal.cpp000066400000000000000000000003461413460502400226220ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_signal_literal) { test("k:f { signal(8); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_signal_string_literal.cpp000066400000000000000000000003671413460502400242130ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_signal_string_literal) { test("k:f { signal(\"SIGKILL\"); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_sizeof.cpp000066400000000000000000000004141413460502400211240ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_sizeof) { test("struct Foo { int x; char c; } BEGIN { @x = sizeof(struct Foo) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_stats.cpp000066400000000000000000000003501413460502400207620ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_stats) { test("kprobe:f { @x = stats(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_str.cpp000066400000000000000000000003451413460502400204400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_str) { test("kprobe:f { @x = str(arg0) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_str_2_expr.cpp000066400000000000000000000003621413460502400217160ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_str_2_expr) { test("kprobe:f { @x = str(arg0, arg1) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_str_2_lit.cpp000066400000000000000000000003561413460502400215330ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_str_2_lit) { test("kprobe:f { @x = str(arg0, 6) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_strftime.cpp000066400000000000000000000003771413460502400214720ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_strftime) { test("kprobe:f { printf(\"%s\", strftime(\"%M:%S\", nsecs)); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_sum.cpp000066400000000000000000000003441413460502400204330ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_sum) { test("kprobe:f { @x = sum(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_system.cpp000066400000000000000000000004011413460502400211450ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_system) { test(" kprobe:f { system(\"echo %d\", 100) }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_time.cpp000066400000000000000000000003371413460502400205670ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_time) { test("kprobe:f { time(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_uptr.cpp000066400000000000000000000004751413460502400206260ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_uptr) { test("k:f { @=*uptr((int16*) arg0 ); }", std::string(NAME) + "_1"); test("k:f { @=*uptr((int32*) arg0 ); }", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_ustack.cpp000066400000000000000000000052051413460502400211220ustar00rootroot00000000000000#include "common.h" #include namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ustack) { auto result = NAME; // Mode doesn't directly affect codegen, so both should generate the same // program test("kprobe:f { @x = ustack(); @y = ustack(6) }", result); test("kprobe:f { @x = ustack(perf); @y = ustack(perf, 6) }", result); test("kprobe:f { @x = ustack(perf); @y = ustack(bpftrace) }", result); } TEST(codegen, call_ustack_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "kprobe:f { @x = ustack(5); @y = ustack(6); @z = ustack(6) }"), 0); ClangParser clang; clang.parse(driver.root_, *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; ast::CodegenLLVM codegen(driver.root_, *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); ASSERT_EQ(bpftrace->maps.CountStackTypes(), 2U); StackType stack_type; stack_type.limit = 5; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); stack_type.limit = 6; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); } TEST(codegen, call_ustack_modes_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str("kprobe:f { @x = ustack(perf); @y = " "ustack(bpftrace); @z = ustack() }"), 0); ClangParser clang; clang.parse(driver.root_, *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; ast::CodegenLLVM codegen(driver.root_, *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); ASSERT_EQ(bpftrace->maps.CountStackTypes(), 2U); StackType stack_type; stack_type.mode = StackMode::perf; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); stack_type.mode = StackMode::bpftrace; ASSERT_TRUE(bpftrace->maps.Has(stack_type)); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_usym_key.cpp000066400000000000000000000003611413460502400214730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_usym_key) { test("kprobe:f { @x[usym(0)] = count() }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/call_zero.cpp000066400000000000000000000003631413460502400206070ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_zero) { test("BEGIN { @x = 1; } kprobe:f { zero(@x); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/common.h000066400000000000000000000050421413460502400175710ustar00rootroot00000000000000#pragma once #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "../mocks.h" #include "ast/bpforc/bpforc.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpffeature.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "fake_map.h" #include "tracepoint_format_parser.h" namespace bpftrace { namespace test { namespace codegen { #define NAME (::testing::UnitTest::GetInstance()->current_test_info()->name()) static std::string get_expected(const std::string &name) { std::string fname = TEST_CODEGEN_LOCATION + name + ".ll"; std::ifstream file; file.open(fname); if (file.good()) return std::string((std::istreambuf_iterator(file)), (std::istreambuf_iterator())); throw std::runtime_error("Could not find codegen result for test: " + name); } static void test(BPFtrace &bpftrace, const std::string &input, const std::string &name) { Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(input), 0); ClangParser clang; clang.parse(driver.root_, bpftrace); ASSERT_EQ(driver.parse_str(input), 0); // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(bpftrace, true), 0); bpftrace.resources = resources; std::stringstream out; ast::CodegenLLVM codegen(driver.root_, bpftrace); codegen.generate_ir(); codegen.DumpIR(out); // Test that generated code compiles cleanly codegen.optimize(); codegen.emit(); uint64_t update_tests = 0; if (get_uint64_env_var("BPFTRACE_UPDATE_TESTS", update_tests) && update_tests >= 1) { std::cerr << "Running in update mode, test is skipped" << std::endl; std::ofstream file(TEST_CODEGEN_LOCATION + name + ".ll"); file << out.str(); return; } std::string expected_output = get_expected(name); EXPECT_EQ(expected_output, out.str()) << "the following program failed: '" << input << "'"; } static void test(const std::string &input, const std::string &name, bool safe_mode = true) { auto bpftrace = get_mock_bpftrace(); bpftrace->safe_mode_ = safe_mode; test(*bpftrace, input, name); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/comparison_extend.cpp000066400000000000000000000004171413460502400223560ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, comparison_extend) { // Make sure i1 is zero extended test("kprobe:f { @ = 1 < arg0 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/dereference.cpp000066400000000000000000000003521413460502400211020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, dereference) { test("kprobe:f { @x = *kptr(1234) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/empty_function.cpp000066400000000000000000000003371413460502400217010ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, empty_function) { test("kprobe:f { 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/enum.cpp000066400000000000000000000003731413460502400176020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, enum_declaration) { test("enum { a = 42, b } k:f { @a = a; @b = b }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/general.cpp000066400000000000000000000071061413460502400202540ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::_; class MockBPFtrace : public BPFtrace { public: #pragma GCC diagnostic push #ifdef __clang__ #pragma GCC diagnostic ignored "-Winconsistent-missing-override" #endif MOCK_METHOD1(add_probe, int(ast::Probe &p)); #pragma GCC diagnostic pop int resolve_uname(const std::string &name, struct symbol *sym, const std::string &path) const override { (void)path; sym->name = name; sym->address = 12345; sym->size = 4; return 0; } bool is_traceable_func( const std::string &__attribute__((unused))) const override { return true; } }; TEST(codegen, populate_sections) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str("kprobe:foo { 1 } kprobe:bar { 1 }"), 0); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::CodegenLLVM codegen(driver.root_, *bpftrace); auto bpforc = codegen.compile(); EXPECT_TRUE(bpforc->getSection("s_kprobe:foo_1").has_value()); EXPECT_TRUE(bpforc->getSection("s_kprobe:bar_1").has_value()); } TEST(codegen, printf_offsets) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "struct Foo { char c; int i; char str[10]; }\n" "kprobe:f\n" "{\n" " $foo = (struct Foo*)arg0;\n" " printf(\"%c %u %s %p\\n\", $foo->c, $foo->i, $foo->str, 0)\n" "}"), 0); ClangParser clang; clang.parse(driver.root_, *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; ast::CodegenLLVM codegen(driver.root_, *bpftrace); codegen.generate_ir(); EXPECT_EQ(resources.printf_args.size(), 1U); auto &fmt = std::get<0>(bpftrace->resources.printf_args[0]); auto &args = std::get<1>(bpftrace->resources.printf_args[0]); EXPECT_EQ(fmt, "%c %u %s %p\n"); EXPECT_EQ(args.size(), 4U); // Note that scalar types are promoted to 64-bits when put into // a perf event buffer EXPECT_EQ(args[0].type.type, Type::integer); EXPECT_EQ(args[0].type.GetSize(), 8U); EXPECT_EQ(args[0].offset, 8); EXPECT_EQ(args[1].type.type, Type::integer); EXPECT_EQ(args[1].type.GetSize(), 8U); EXPECT_EQ(args[1].offset, 16); EXPECT_EQ(args[2].type.type, Type::string); EXPECT_EQ(args[2].type.GetSize(), 10U); EXPECT_EQ(args[2].offset, 24); EXPECT_EQ(args[3].type.type, Type::integer); EXPECT_EQ(args[3].type.GetSize(), 8U); EXPECT_EQ(args[3].offset, 40); } TEST(codegen, probe_count) { MockBPFtrace bpftrace; EXPECT_CALL(bpftrace, add_probe(_)).Times(2); Driver driver(bpftrace); ASSERT_EQ(driver.parse_str("kprobe:f { 1; } kprobe:d { 1; }"), 0); // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::CodegenLLVM codegen(driver.root_, bpftrace); codegen.generate_ir(); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/generate_codegen_includes.cmake000066400000000000000000000007011413460502400242730ustar00rootroot00000000000000# Combine all codegen tests into a single compilation unit to improve build # performance. https://github.com/iovisor/bpftrace/issues/229 function(generate_codegen_includes output) file(REMOVE ${output}) file(GLOB tests ${TEST_SRC_DIR}/*.cpp) file(WRITE ${output} "") foreach(test ${tests}) file(APPEND ${output} "#include \"${test}\"\n") endforeach() endfunction() generate_codegen_includes(${CMAKE_BINARY_DIR}/codegen_includes.cpp) bpftrace-0.14.0/tests/codegen/if_else_printf.cpp000066400000000000000000000004471413460502400216300ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_else_printf) { test("kprobe:f { if (pid > 10) { printf(\"hi\\n\"); } else " "{printf(\"hello\\n\")} }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/if_else_variable.cpp000066400000000000000000000004551413460502400221120ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_else_variable) { test("kprobe:f { if (pid > 10000) { $s = 10 } else { $s = 20 } printf(\"s = " "%d\", $s) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/if_nested_printf.cpp000066400000000000000000000004451413460502400221600ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_nested_printf) { test("kprobe:f { if (pid > 10000) { if (pid % 2 == 0) { printf(\"hi\\n\");} " "} }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/if_printf.cpp000066400000000000000000000004141413460502400206120ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_printf) { test("kprobe:f { if (pid > 10000) { printf(\"%d is high\\n\", pid); } }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/if_variable.cpp000066400000000000000000000004161413460502400210770ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_variable) { test("kprobe:f { if (pid > 10000) { $s = 10 } printf(\"s = %d\", $s); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/int_propagation.cpp000066400000000000000000000003601413460502400220270ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, int_propagation) { test("kprobe:f { @x = 1234; @y = @x }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/intcast_assign_var.cpp000066400000000000000000000004601413460502400225140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, intcast_retval) { // Make sure the result is truncated to 32 bit and sign extended to 64 test("kretprobe:f { @=(int32)retval }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/intcast_retval.cpp000066400000000000000000000004211413460502400216520ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, intcast_call) { // Casting should work inside a call test("kretprobe:f { @=sum((int32)retval) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/intptrcast_assign_var.cpp000066400000000000000000000004221413460502400232400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { #ifdef ARCH_X86_64 TEST(codegen, intptrcast_assign_var) { test("kretprobe:f { @=*(int8*)(reg(\"bp\")-1) }", NAME); } #endif } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/intptrcast_call.cpp000066400000000000000000000004701413460502400220220ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { #ifdef ARCH_X86_64 TEST(codegen, intptrcast_call) { // Casting should work inside a call test("kretprobe:f { @=sum(*(int8*)(reg(\"bp\")-1)) }", NAME); } #endif } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/literal_strncmp.cpp000066400000000000000000000004271413460502400220400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, literal_strncmp) { test("kretprobe:vfs_read /strncmp(comm, \"sshd\", 2)/ { @[comm] = count(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/llvm/000077500000000000000000000000001413460502400171015ustar00rootroot00000000000000bpftrace-0.14.0/tests/codegen/llvm/args_multiple_tracepoints.ll000066400000000000000000000106141413460502400247160ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:sched:sched_one"(i8* %0) section "s_tracepoint:sched:sched_one_1" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = add i64 %1, 8 %3 = inttoptr i64 %2 to i64* %4 = load volatile i64, i64* %3, align 8 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %12 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 define i64 @"tracepoint:sched:sched_two"(i8* %0) section "s_tracepoint:sched:sched_two_2" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = add i64 %1, 16 %3 = inttoptr i64 %2 to i64* %4 = load volatile i64, i64* %3, align 8 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %12 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 1 } attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/args_multiple_tracepoints_category_wild.ll000066400000000000000000000144711413460502400276370ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:sched:sched_one"(i8* %0) section "s_tracepoint:sched:sched_one_1" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = add i64 %1, 8 %3 = inttoptr i64 %2 to i64* %4 = load volatile i64, i64* %3, align 8 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %12 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 define i64 @"tracepoint:sched:sched_two"(i8* %0) section "s_tracepoint:sched:sched_two_2" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = add i64 %1, 16 %3 = inttoptr i64 %2 to i64* %4 = load volatile i64, i64* %3, align 8 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %12 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 1 } define i64 @"tracepoint:sched_extra:sched_extra"(i8* %0) section "s_tracepoint:sched_extra:sched_extra_3" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = add i64 %1, 24 %3 = inttoptr i64 %2 to i64* %4 = load volatile i64, i64* %3, align 8 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %12 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 1 } attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/args_multiple_tracepoints_wild.ll000066400000000000000000000106141413460502400257350ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:sched:sched_one"(i8* %0) section "s_tracepoint:sched:sched_one_1" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = add i64 %1, 8 %3 = inttoptr i64 %2 to i64* %4 = load volatile i64, i64* %3, align 8 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %12 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 define i64 @"tracepoint:sched:sched_two"(i8* %0) section "s_tracepoint:sched:sched_two_2" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = add i64 %1, 16 %3 = inttoptr i64 %2 to i64* %4 = load volatile i64, i64* %3, align 8 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %12 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 1 } attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/basic_while_loop.ll000066400000000000000000000041671413460502400227440ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"interval:s:1"(i8* %0) section "s_interval:s:1_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %"$a" = alloca i64, align 8 %1 = bitcast i64* %"$a" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$a", align 8 store i64 1, i64* %"$a", align 8 br label %while_cond while_cond: ; preds = %while_body, %entry %2 = load i64, i64* %"$a", align 8 %3 = icmp sle i64 %2, 150 %4 = zext i1 %3 to i64 %true_cond = icmp ne i64 %4, 0 br i1 %true_cond, label %while_body, label %while_end, !llvm.loop !0 while_body: ; preds = %while_cond %5 = load i64, i64* %"$a", align 8 %6 = add i64 %5, 1 store i64 %6, i64* %"$a", align 8 %7 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@_key", align 8 %8 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %5, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %9 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) br label %while_cond while_end: ; preds = %while_cond ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } !0 = distinct !{!0, !1} !1 = !{!"llvm.loop.unroll.disable"} bpftrace-0.14.0/tests/codegen/llvm/bitshift_left.ll000066400000000000000000000025201413460502400222570ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 1024, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/bitshift_right.ll000066400000000000000000000025151413460502400224460ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 2, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/bitwise_not.ll000066400000000000000000000025071413460502400217640ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 -11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_arg.ll000066400000000000000000000043661413460502400217420ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %arg0, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i8* %0 to i64* %8 = getelementptr i64, i64* %7, i64 12 %arg2 = load volatile i64, i64* %8, align 8 %9 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@y_key", align 8 %10 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 %arg2, i64* %"@y_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem2 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@y_key", i64* %"@y_val", i64 0) %11 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_comm.ll000066400000000000000000000033661413460502400221230ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %comm = alloca [16 x i8], align 1 %1 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [16 x i8]* %comm to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm, i64 16) %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [16 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [16 x i8]* %comm, i64 0) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/builtin_cpid.ll000066400000000000000000000025061413460502400221020ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@_key", align 8 %2 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 1337, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %3 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_cpu.ll000066400000000000000000000026161413460502400217540ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to i64 ()*)() %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 %get_cpu_id, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_ctx.ll000066400000000000000000000025541413460502400217640ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = ptrtoint i8* %0 to i64 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 %1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_ctx_field.ll000066400000000000000000000127011413460502400231220ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@e_key" = alloca i64, align 8 %"struct x.e" = alloca [4 x i8], align 1 %"@d_val" = alloca i64, align 8 %"@d_key" = alloca i64, align 8 %"struct c.c" = alloca i8, align 1 %"@c_val" = alloca i64, align 8 %"@c_key" = alloca i64, align 8 %"@b_val" = alloca i64, align 8 %"@b_key" = alloca i64, align 8 %"@a_val" = alloca i64, align 8 %"@a_key" = alloca i64, align 8 %"$x" = alloca i64, align 8 %1 = bitcast i64* %"$x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$x", align 8 %2 = ptrtoint i8* %0 to i64 store i64 %2, i64* %"$x", align 8 %3 = load i64, i64* %"$x", align 8 %4 = add i64 %3, 0 %5 = inttoptr i64 %4 to i64* %6 = load volatile i64, i64* %5, align 8 %7 = bitcast i64* %"@a_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@a_key", align 8 %8 = bitcast i64* %"@a_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %6, i64* %"@a_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@a_key", i64* %"@a_val", i64 0) %9 = bitcast i64* %"@a_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@a_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = load i64, i64* %"$x", align 8 %12 = add i64 %11, 8 %13 = add i64 %12, 0 %14 = inttoptr i64 %13 to i16* %15 = load volatile i16, i16* %14, align 2 %16 = sext i16 %15 to i64 %17 = bitcast i64* %"@b_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %17) store i64 0, i64* %"@b_key", align 8 %18 = bitcast i64* %"@b_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %18) store i64 %16, i64* %"@b_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem2 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@b_key", i64* %"@b_val", i64 0) %19 = bitcast i64* %"@b_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) %20 = bitcast i64* %"@b_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) %21 = load i64, i64* %"$x", align 8 %22 = add i64 %21, 16 %23 = add i64 %22, 0 %24 = inttoptr i64 %23 to i8* %25 = load volatile i8, i8* %24, align 1 %26 = sext i8 %25 to i64 %27 = bitcast i64* %"@c_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %27) store i64 0, i64* %"@c_key", align 8 %28 = bitcast i64* %"@c_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %28) store i64 %26, i64* %"@c_val", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 2) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo3, i64* %"@c_key", i64* %"@c_val", i64 0) %29 = bitcast i64* %"@c_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %29) %30 = bitcast i64* %"@c_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %30) %31 = load i64, i64* %"$x", align 8 %32 = add i64 %31, 24 %33 = inttoptr i64 %32 to i64* %34 = load volatile i64, i64* %33, align 8 %35 = add i64 %34, 0 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct c.c") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct c.c", i32 1, i64 %35) %36 = load i8, i8* %"struct c.c", align 1 %37 = sext i8 %36 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct c.c") %38 = bitcast i64* %"@d_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %38) store i64 0, i64* %"@d_key", align 8 %39 = bitcast i64* %"@d_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %39) store i64 %37, i64* %"@d_val", align 8 %pseudo5 = call i64 @llvm.bpf.pseudo(i64 1, i64 3) %update_elem6 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo5, i64* %"@d_key", i64* %"@d_val", i64 0) %40 = bitcast i64* %"@d_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %40) %41 = bitcast i64* %"@d_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %41) %42 = load i64, i64* %"$x", align 8 %43 = add i64 %42, 32 %44 = bitcast [4 x i8]* %"struct x.e" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %44) %probe_read_kernel7 = call i64 inttoptr (i64 113 to i64 ([4 x i8]*, i32, i64)*)([4 x i8]* %"struct x.e", i32 4, i64 %43) %45 = bitcast i64* %"@e_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %45) store i64 0, i64* %"@e_key", align 8 %pseudo8 = call i64 @llvm.bpf.pseudo(i64 1, i64 4) %update_elem9 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [4 x i8]*, i64)*)(i64 %pseudo8, i64* %"@e_key", [4 x i8]* %"struct x.e", i64 0) %46 = bitcast i64* %"@e_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %46) %47 = bitcast [4 x i8]* %"struct x.e" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %47) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_curtask.ll000066400000000000000000000026231413460502400226370ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_ptr" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_cur_task = call i64 inttoptr (i64 35 to i64 ()*)() %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %2 = bitcast i64* %"@x_ptr" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 %get_cur_task, i64* %"@x_ptr", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_ptr", i64 0) %3 = bitcast i64* %"@x_ptr" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_elapsed.ll000066400000000000000000000052311413460502400225760ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"interval:s:1"(i8* %0) section "s_interval:s:1_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %elapsed_key = alloca i64, align 8 %1 = bitcast i64* %elapsed_key to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %elapsed_key, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %elapsed_key) %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %get_ns = call i64 inttoptr (i64 125 to i64 ()*)() %6 = sub i64 %get_ns, %4 %7 = bitcast i64* %elapsed_key to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 0, i64* %"@_key", align 8 %9 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 %6, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %10 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_func.ll000066400000000000000000000027071413460502400221210ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 16 %func = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %func, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_func_uprobe.ll000066400000000000000000000033441413460502400234730ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %usym_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"uprobe:/bin/sh:f"(i8* %0) section "s_uprobe:/bin/sh:f_1" { entry: %"@x_key" = alloca i64, align 8 %usym = alloca %usym_t, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 16 %func = load volatile i64, i64* %2, align 8 %3 = bitcast %usym_t* %usym to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %4 = lshr i64 %get_pid_tgid, 32 %5 = getelementptr %usym_t, %usym_t* %usym, i64 0, i32 0 %6 = getelementptr %usym_t, %usym_t* %usym, i64 0, i32 1 store i64 %func, i64* %5, align 8 store i64 %4, i64* %6, align 8 %7 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %usym_t*, i64)*)(i64 %pseudo, i64* %"@x_key", %usym_t* %usym, i64 0) %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast %usym_t* %usym to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_func_wild.ll000066400000000000000000000027311413460502400231350ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:do_execve*"(i8* %0) section "s_kprobe:do_execve*_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 16 %func = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %func, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_kstack.ll000066400000000000000000000027561413460502400224520ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %get_stackid = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo, i64 0) %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 %get_stackid, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_nsecs.ll000066400000000000000000000026101413460502400222720ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_ns = call i64 inttoptr (i64 125 to i64 ()*)() %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 %get_ns, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_pid_tid.ll000066400000000000000000000043011413460502400225720ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 %1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %get_pid_tgid1 = call i64 inttoptr (i64 14 to i64 ()*)() %6 = and i64 %get_pid_tgid1, 4294967295 %7 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@y_key", align 8 %8 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %6, i64* %"@y_val", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem3 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@y_key", i64* %"@y_val", i64 0) %9 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_probe.ll000066400000000000000000000025611413460502400222730ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:sched:sched_one"(i8* %0) section "s_tracepoint:sched:sched_one_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 0, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_probe_wild.ll000066400000000000000000000025611413460502400233120ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:sched:sched_one"(i8* %0) section "s_tracepoint:sched:sched_one_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 0, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_rand.ll000066400000000000000000000026161413460502400221110ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_random = call i64 inttoptr (i64 7 to i64 ()*)() %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 %get_random, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_retval.ll000066400000000000000000000027211413460502400224570ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:f"(i8* %0) section "s_kretprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 10 %retval = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %retval, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_sarg.ll000066400000000000000000000057621413460502400221260ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %sarg2 = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %sarg0 = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 19 %reg_sp = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %sarg0 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %4 = add i64 %reg_sp, 8 %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %sarg0, i32 8, i64 %4) %5 = load i64, i64* %sarg0, align 8 %6 = bitcast i64* %sarg0 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@x_key", align 8 %8 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %5, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i8* %0 to i64* %12 = getelementptr i64, i64* %11, i64 19 %reg_sp1 = load volatile i64, i64* %12, align 8 %13 = bitcast i64* %sarg2 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) %14 = add i64 %reg_sp1, 24 %probe_read_kernel2 = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %sarg2, i32 8, i64 %14) %15 = load i64, i64* %sarg2, align 8 %16 = bitcast i64* %sarg2 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) %17 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %17) store i64 0, i64* %"@y_key", align 8 %18 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %18) store i64 %15, i64* %"@y_val", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo3, i64* %"@y_key", i64* %"@y_val", i64 0) %19 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) %20 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_uid_gid.ll000066400000000000000000000042751413460502400225740ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_uid_gid = call i64 inttoptr (i64 15 to i64 ()*)() %1 = and i64 %get_uid_gid, 4294967295 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 %1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %get_uid_gid1 = call i64 inttoptr (i64 15 to i64 ()*)() %6 = lshr i64 %get_uid_gid1, 32 %7 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@y_key", align 8 %8 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %6, i64* %"@y_val", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem3 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@y_key", i64* %"@y_val", i64 0) %9 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_username.ll000066400000000000000000000042751413460502400230070ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_uid_gid = call i64 inttoptr (i64 15 to i64 ()*)() %1 = and i64 %get_uid_gid, 4294967295 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 %1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %get_uid_gid1 = call i64 inttoptr (i64 15 to i64 ()*)() %6 = lshr i64 %get_uid_gid1, 32 %7 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@y_key", align 8 %8 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %6, i64* %"@y_val", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem3 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@y_key", i64* %"@y_val", i64 0) %9 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/builtin_ustack.ll000066400000000000000000000031401413460502400224500ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %get_stackid = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo, i64 256) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = shl i64 %get_pid_tgid, 32 %2 = or i64 %get_stackid, %1 %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %2, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_avg.ll000066400000000000000000000102371413460502400212050ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val8 = alloca i64, align 8 %"@x_key2" = alloca i64, align 8 %"@x_num" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_num" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = add i64 %4, 1 store i64 %7, i64* %"@x_num", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_num", i64 0) %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@x_num" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key2" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 1, i64* %"@x_key2", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem4 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo3, i64* %"@x_key2") %11 = bitcast i64* %lookup_elem_val8 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) %map_lookup_cond9 = icmp ne i8* %lookup_elem4, null br i1 %map_lookup_cond9, label %lookup_success5, label %lookup_failure6 lookup_success5: ; preds = %lookup_merge %cast10 = bitcast i8* %lookup_elem4 to i64* %12 = load i64, i64* %cast10, align 8 store i64 %12, i64* %lookup_elem_val8, align 8 br label %lookup_merge7 lookup_failure6: ; preds = %lookup_merge store i64 0, i64* %lookup_elem_val8, align 8 br label %lookup_merge7 lookup_merge7: ; preds = %lookup_failure6, %lookup_success5 %13 = load i64, i64* %lookup_elem_val8, align 8 %14 = bitcast i64* %lookup_elem_val8 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %16 = lshr i64 %get_pid_tgid, 32 %17 = add i64 %16, %13 store i64 %17, i64* %"@x_val", align 8 %pseudo11 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem12 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo11, i64* %"@x_key2", i64* %"@x_val", i64 0) %18 = bitcast i64* %"@x_key2" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) %19 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_buf_implicit_size.ll000066400000000000000000000043631413460502400241330ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %buffer_16_t = type { i8, [16 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %buffer = alloca %buffer_16_t, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 store i64 0, i64* %"$foo", align 8 %2 = bitcast %buffer_16_t* %buffer to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %3 = getelementptr %buffer_16_t, %buffer_16_t* %buffer, i32 0, i32 0 store i8 16, i8* %3, align 1 %4 = getelementptr %buffer_16_t, %buffer_16_t* %buffer, i32 0, i32 1 %5 = bitcast [16 x i8]* %4 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 16, i1 false) %6 = load i64, i64* %"$foo", align 8 %7 = add i64 %6, 0 %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([16 x i8]*, i32, i64)*)([16 x i8]* %4, i32 16, i64 %7) %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %buffer_16_t*, i64)*)(i64 %pseudo, i64* %"@x_key", %buffer_16_t* %buffer, i64 0) %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast %buffer_16_t* %buffer to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_buf_size_literal.ll000066400000000000000000000041321413460502400237470ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %buffer_1_t = type { i8, [1 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %buffer = alloca %buffer_1_t, align 8 %1 = bitcast %buffer_1_t* %buffer to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %buffer_1_t, %buffer_1_t* %buffer, i32 0, i32 0 store i8 1, i8* %2, align 1 %3 = getelementptr %buffer_1_t, %buffer_1_t* %buffer, i32 0, i32 1 %4 = bitcast [1 x i8]* %3 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 1, i1 false) %5 = bitcast i8* %0 to i64* %6 = getelementptr i64, i64* %5, i64 14 %arg0 = load volatile i64, i64* %6, align 8 %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([1 x i8]*, i32, i64)*)([1 x i8]* %3, i32 1, i64 %arg0) %7 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %buffer_1_t*, i64)*)(i64 %pseudo, i64* %"@x_key", %buffer_1_t* %buffer, i64 0) %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast %buffer_1_t* %buffer to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_buf_size_nonliteral.ll000066400000000000000000000046131413460502400244660ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %buffer_64_t = type { i8, [64 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %buffer = alloca %buffer_64_t, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 13 %arg1 = load volatile i64, i64* %2, align 8 %length.cmp = icmp ule i64 %arg1, 64 %length.select = select i1 %length.cmp, i64 %arg1, i64 64 %3 = bitcast %buffer_64_t* %buffer to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %4 = getelementptr %buffer_64_t, %buffer_64_t* %buffer, i32 0, i32 0 %5 = trunc i64 %length.select to i8 store i8 %5, i8* %4, align 1 %6 = getelementptr %buffer_64_t, %buffer_64_t* %buffer, i32 0, i32 1 %7 = bitcast [64 x i8]* %6 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %7, i8 0, i64 64, i1 false) %8 = bitcast i8* %0 to i64* %9 = getelementptr i64, i64* %8, i64 14 %arg0 = load volatile i64, i64* %9, align 8 %10 = zext i8 %5 to i32 %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %6, i32 %10, i64 %arg0) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %buffer_64_t*, i64)*)(i64 %pseudo, i64* %"@x_key", %buffer_64_t* %buffer, i64 0) %12 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast %buffer_64_t* %buffer to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_cat.ll000066400000000000000000000030501413460502400211720ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %cat_t = type { i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %cat_args = alloca %cat_t, align 8 %1 = bitcast %cat_t* %cat_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %cat_t* %cat_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 8, i1 false) %3 = getelementptr %cat_t, %cat_t* %cat_args, i32 0, i32 0 store i64 20000, i64* %3, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %cat_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %cat_t* %cat_args, i64 8) %4 = bitcast %cat_t* %cat_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_cgroup.ll000066400000000000000000000034741413460502400217340ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:syscalls:sys_enter_openat"(i8* %0) section "s_tracepoint:syscalls:sys_enter_openat_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_cgroup_id = call i64 inttoptr (i64 80 to i64 ()*)() %1 = icmp eq i64 %get_cgroup_id, 4294967297 %2 = zext i1 %1 to i64 %predcond = icmp eq i64 %2, 0 br i1 %predcond, label %pred_false, label %pred_true pred_false: ; preds = %entry ret i64 1 pred_true: ; preds = %entry %get_cgroup_id1 = call i64 inttoptr (i64 80 to i64 ()*)() %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %get_cgroup_id1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_clear.ll000066400000000000000000000040721413460502400215160ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %clear_t = type <{ i64, i32 }> ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"clear_@x" = alloca %clear_t, align 8 %1 = bitcast %clear_t* %"clear_@x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %clear_t, %clear_t* %"clear_@x", i64 0, i32 0 store i64 30002, i64* %2, align 8 %3 = getelementptr %clear_t, %clear_t* %"clear_@x", i64 0, i32 1 store i32 0, i32* %3, align 4 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %clear_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %clear_t* %"clear_@x", i64 12) %4 = bitcast %clear_t* %"clear_@x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_count.ll000066400000000000000000000045201413460502400215560ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = add i64 %4, 1 store i64 %7, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) %8 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_delete.ll000066400000000000000000000033411413460502400216700ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %delete_elem = call i64 inttoptr (i64 3 to i64 (i64, i64*)*)(i64 %pseudo2, i64* %"@x_key1") %6 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_exit.ll000066400000000000000000000035361413460502400214050ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %perfdata = alloca i64, align 8 %1 = bitcast i64* %perfdata to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 30000, i64* %perfdata, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, i64*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, i64* %perfdata, i64 8) %2 = bitcast i64* %perfdata to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %2) ret i64 0 deadcode: ; No predecessors! %3 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@_key", align 8 %4 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 10, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %5 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_hist.ll000066400000000000000000000116731413460502400214040ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %log2 = call i64 @log2(i64 %1) %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 %log2, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %3 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %4 = load i64, i64* %cast, align 8 store i64 %4, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %5 = load i64, i64* %lookup_elem_val, align 8 %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %8 = add i64 %5, 1 store i64 %8, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: alwaysinline define internal i64 @log2(i64 %0) #1 section "helpers" { entry: %1 = alloca i64, align 8 %2 = alloca i64, align 8 %3 = bitcast i64* %2 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 %0, i64* %2, align 8 %4 = bitcast i64* %1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 0, i64* %1, align 8 %5 = load i64, i64* %2, align 8 %6 = icmp slt i64 %5, 0 br i1 %6, label %hist.is_less_than_zero, label %hist.is_not_less_than_zero hist.is_less_than_zero: ; preds = %entry %7 = load i64, i64* %1, align 8 ret i64 %7 hist.is_not_less_than_zero: ; preds = %entry %8 = load i64, i64* %2, align 8 %9 = icmp eq i64 %8, 0 br i1 %9, label %hist.is_zero, label %hist.is_not_zero hist.is_zero: ; preds = %hist.is_not_less_than_zero store i64 1, i64* %1, align 8 %10 = load i64, i64* %1, align 8 ret i64 %10 hist.is_not_zero: ; preds = %hist.is_not_less_than_zero store i64 2, i64* %1, align 8 %11 = load i64, i64* %2, align 8 %12 = icmp sge i64 %11, 65536 %13 = zext i1 %12 to i64 %14 = shl i64 %13, 4 %15 = lshr i64 %11, %14 store i64 %15, i64* %2, align 8 %16 = load i64, i64* %1, align 8 %17 = add i64 %16, %14 store i64 %17, i64* %1, align 8 %18 = load i64, i64* %2, align 8 %19 = icmp sge i64 %18, 256 %20 = zext i1 %19 to i64 %21 = shl i64 %20, 3 %22 = lshr i64 %18, %21 store i64 %22, i64* %2, align 8 %23 = load i64, i64* %1, align 8 %24 = add i64 %23, %21 store i64 %24, i64* %1, align 8 %25 = load i64, i64* %2, align 8 %26 = icmp sge i64 %25, 16 %27 = zext i1 %26 to i64 %28 = shl i64 %27, 2 %29 = lshr i64 %25, %28 store i64 %29, i64* %2, align 8 %30 = load i64, i64* %1, align 8 %31 = add i64 %30, %28 store i64 %31, i64* %1, align 8 %32 = load i64, i64* %2, align 8 %33 = icmp sge i64 %32, 4 %34 = zext i1 %33 to i64 %35 = shl i64 %34, 1 %36 = lshr i64 %32, %35 store i64 %36, i64* %2, align 8 %37 = load i64, i64* %1, align 8 %38 = add i64 %37, %35 store i64 %38, i64* %1, align 8 %39 = load i64, i64* %2, align 8 %40 = icmp sge i64 %39, 2 %41 = zext i1 %40 to i64 %42 = shl i64 %41, 0 %43 = lshr i64 %39, %42 store i64 %43, i64* %2, align 8 %44 = load i64, i64* %1, align 8 %45 = add i64 %44, %42 store i64 %45, i64* %1, align 8 %46 = load i64, i64* %1, align 8 ret i64 %46 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_kstack.ll000066400000000000000000000045001413460502400217040ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 3) %get_stackid = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo, i64 0) %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 %get_stackid, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 2) %get_stackid3 = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo2, i64 0) %5 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@y_key", align 8 %6 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) store i64 %get_stackid3, i64* %"@y_val", align 8 %pseudo4 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem5 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo4, i64* %"@y_key", i64* %"@y_val", i64 0) %7 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_lhist.ll000066400000000000000000000110161413460502400215470ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %get_pid_tgid1 = call i64 inttoptr (i64 14 to i64 ()*)() %2 = lshr i64 %get_pid_tgid1, 32 %linear = call i64 @linear(i64 %2, i64 0, i64 100, i64 1) %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 %linear, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %4 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %5 = load i64, i64* %cast, align 8 store i64 %5, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %6 = load i64, i64* %lookup_elem_val, align 8 %7 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) %9 = add i64 %6, 1 store i64 %9, i64* %"@x_val", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@x_key", i64* %"@x_val", i64 0) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 0 } ; Function Attrs: alwaysinline define internal i64 @linear(i64 %0, i64 %1, i64 %2, i64 %3) #1 section "helpers" { entry: %4 = alloca i64, align 8 %5 = alloca i64, align 8 %6 = alloca i64, align 8 %7 = alloca i64, align 8 %8 = alloca i64, align 8 %9 = bitcast i64* %8 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %7 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %6 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %5 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %4 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) store i64 %0, i64* %8, align 8 store i64 %1, i64* %7, align 8 store i64 %2, i64* %6, align 8 store i64 %3, i64* %5, align 8 %14 = load i64, i64* %7, align 8 %15 = load i64, i64* %8, align 8 %16 = icmp slt i64 %15, %14 br i1 %16, label %lhist.lt_min, label %lhist.ge_min lhist.lt_min: ; preds = %entry ret i64 0 lhist.ge_min: ; preds = %entry %17 = load i64, i64* %6, align 8 %18 = load i64, i64* %8, align 8 %19 = icmp sgt i64 %18, %17 br i1 %19, label %lhist.gt_max, label %lhist.le_max lhist.le_max: ; preds = %lhist.ge_min %20 = load i64, i64* %5, align 8 %21 = load i64, i64* %7, align 8 %22 = load i64, i64* %8, align 8 %23 = sub i64 %22, %21 %24 = udiv i64 %23, %20 %25 = add i64 %24, 1 store i64 %25, i64* %4, align 8 %26 = load i64, i64* %4, align 8 ret i64 %26 lhist.gt_max: ; preds = %lhist.ge_min %27 = load i64, i64* %5, align 8 %28 = load i64, i64* %7, align 8 %29 = load i64, i64* %6, align 8 %30 = sub i64 %29, %28 %31 = udiv i64 %30, %27 %32 = add i64 %31, 1 store i64 %32, i64* %4, align 8 %33 = load i64, i64* %4, align 8 ret i64 %33 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_macaddr.ll000066400000000000000000000034241413460502400220230ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %macaddr = alloca [6 x i8], align 1 %1 = bitcast [6 x i8]* %macaddr to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [6 x i8]* %macaddr to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 6, i1 false) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([6 x i8]*, i32, i64)*)([6 x i8]* %macaddr, i32 6, i64 0) %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [6 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [6 x i8]* %macaddr, i64 0) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast [6 x i8]* %macaddr to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_max.ll000066400000000000000000000052171413460502400212170ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %7 = lshr i64 %get_pid_tgid, 32 %8 = icmp sge i64 %7, %4 br i1 %8, label %min.ge, label %min.lt min.lt: ; preds = %min.ge, %lookup_merge %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 min.ge: ; preds = %lookup_merge store i64 %7, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) br label %min.lt } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_min.ll000066400000000000000000000052571413460502400212210ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %7 = lshr i64 %get_pid_tgid, 32 %8 = sub i64 4294967295, %7 %9 = icmp sge i64 %8, %4 br i1 %9, label %min.ge, label %min.lt min.lt: ; preds = %min.ge, %lookup_merge %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 0 min.ge: ; preds = %lookup_merge store i64 %8, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) br label %min.lt } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_ntop_char16.ll000066400000000000000000000036661413460502400225640ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %inet_t = type { i64, [16 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %inet = alloca %inet_t, align 8 %1 = bitcast %inet_t* %inet to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %inet_t, %inet_t* %inet, i64 0, i32 0 store i64 10, i64* %2, align 8 %3 = getelementptr %inet_t, %inet_t* %inet, i32 0, i32 1 %4 = bitcast [16 x i8]* %3 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 16, i1 false) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([16 x i8]*, i32, i64)*)([16 x i8]* %3, i32 16, i64 0) %5 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %inet_t*, i64)*)(i64 %pseudo, i64* %"@x_key", %inet_t* %inet, i64 0) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast %inet_t* %inet to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_ntop_char4.ll000066400000000000000000000036641413460502400224770ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %inet_t = type { i64, [16 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %inet = alloca %inet_t, align 8 %1 = bitcast %inet_t* %inet to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %inet_t, %inet_t* %inet, i64 0, i32 0 store i64 2, i64* %2, align 8 %3 = getelementptr %inet_t, %inet_t* %inet, i32 0, i32 1 %4 = bitcast [16 x i8]* %3 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 16, i1 false) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([16 x i8]*, i32, i64)*)([16 x i8]* %3, i32 4, i64 0) %5 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %inet_t*, i64)*)(i64 %pseudo, i64* %"@x_key", %inet_t* %inet, i64 0) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast %inet_t* %inet to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_ntop_key.ll000066400000000000000000000056321413460502400222630ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %inet_t = type { i64, [16 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %inet = alloca %inet_t, align 8 %1 = bitcast %inet_t* %inet to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %inet_t, %inet_t* %inet, i64 0, i32 0 store i64 2, i64* %2, align 8 %3 = getelementptr %inet_t, %inet_t* %inet, i32 0, i32 1 %4 = bitcast [16 x i8]* %3 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 16, i1 false) %5 = bitcast [16 x i8]* %3 to i32* store i32 -1, i32* %5, align 4 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, %inet_t*)*)(i64 %pseudo, %inet_t* %inet) %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, %inet_t*, i64*, i64)*)(i64 %pseudo1, %inet_t* %inet, i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast %inet_t* %inet to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_override.ll000066400000000000000000000010211413460502400222360ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %override = call i64 inttoptr (i64 58 to i64 (i8*, i64)*)(i8* %0, i64 %arg0) ret i64 0 } attributes #0 = { nounwind } bpftrace-0.14.0/tests/codegen/llvm/call_override_literal.ll000066400000000000000000000006301413460502400237570ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %override = call i64 inttoptr (i64 58 to i64 (i8*, i64)*)(i8* %0, i64 -1) ret i64 0 } attributes #0 = { nounwind } bpftrace-0.14.0/tests/codegen/llvm/call_print.ll000066400000000000000000000044121413460502400215620ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %print_t = type <{ i64, i32, i32, i32 }> ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"print_@x" = alloca %print_t, align 8 %1 = bitcast %print_t* %"print_@x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %print_t, %print_t* %"print_@x", i64 0, i32 0 store i64 30001, i64* %2, align 8 %3 = getelementptr %print_t, %print_t* %"print_@x", i64 0, i32 1 store i32 0, i32* %3, align 4 %4 = getelementptr %print_t, %print_t* %"print_@x", i64 0, i32 2 store i32 0, i32* %4, align 4 %5 = getelementptr %print_t, %print_t* %"print_@x", i64 0, i32 3 store i32 0, i32* %5, align 4 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %print_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %print_t* %"print_@x", i64 20) %6 = bitcast %print_t* %"print_@x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_print_composit.ll000066400000000000000000000071051413460502400235010ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %print_tuple_72_t = type <{ i64, i64, [72 x i8] }> %"int64_string[64]__tuple_t" = type { i64, [64 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %print_tuple_72_t = alloca %print_tuple_72_t, align 8 %str = alloca [64 x i8], align 1 %tuple = alloca %"int64_string[64]__tuple_t", align 8 %1 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 72, i1 false) %3 = getelementptr %"int64_string[64]__tuple_t", %"int64_string[64]__tuple_t"* %tuple, i32 0, i32 0 store i64 1, i64* %3, align 8 %4 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store [64 x i8] c"abc\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 %5 = getelementptr %"int64_string[64]__tuple_t", %"int64_string[64]__tuple_t"* %tuple, i32 0, i32 1 %6 = bitcast [64 x i8]* %5 to i8* %7 = bitcast [64 x i8]* %str to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %6, i8* align 1 %7, i64 64, i1 false) %8 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast %print_tuple_72_t* %print_tuple_72_t to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %10 = getelementptr %print_tuple_72_t, %print_tuple_72_t* %print_tuple_72_t, i64 0, i32 0 store i64 30007, i64* %10, align 8 %11 = getelementptr %print_tuple_72_t, %print_tuple_72_t* %print_tuple_72_t, i64 0, i32 1 store i64 0, i64* %11, align 8 %12 = getelementptr %print_tuple_72_t, %print_tuple_72_t* %print_tuple_72_t, i32 0, i32 2 %13 = bitcast [72 x i8]* %12 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %13, i8 0, i64 72, i1 false) %14 = bitcast [72 x i8]* %12 to i8* %15 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %14, i8* align 1 %15, i64 72, i1 false) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %print_tuple_72_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %print_tuple_72_t* %print_tuple_72_t, i64 88) %16 = bitcast %print_tuple_72_t* %print_tuple_72_t to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) %17 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %17) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_print_int.ll000066400000000000000000000037421413460502400224410ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %print_integer_8_t = type <{ i64, i64, [8 x i8] }> ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %print_integer_8_t = alloca %print_integer_8_t, align 8 %1 = bitcast %print_integer_8_t* %print_integer_8_t to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %print_integer_8_t, %print_integer_8_t* %print_integer_8_t, i64 0, i32 0 store i64 30007, i64* %2, align 8 %3 = getelementptr %print_integer_8_t, %print_integer_8_t* %print_integer_8_t, i64 0, i32 1 store i64 0, i64* %3, align 8 %4 = getelementptr %print_integer_8_t, %print_integer_8_t* %print_integer_8_t, i32 0, i32 2 %5 = bitcast [8 x i8]* %4 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 8, i1 false) %6 = bitcast [8 x i8]* %4 to i64* store i64 3, i64* %6, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %print_integer_8_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %print_integer_8_t* %print_integer_8_t, i64 24) %7 = bitcast %print_integer_8_t* %print_integer_8_t to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_printf.ll000066400000000000000000000057331413460502400217370ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"struct Foo.l" = alloca i64, align 8 %"struct Foo.c" = alloca i8, align 1 %printf_args = alloca %printf_t, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 24, i1 false) %6 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %6, align 8 %7 = load i64, i64* %"$foo", align 8 %8 = add i64 %7, 0 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct Foo.c") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct Foo.c", i32 1, i64 %8) %9 = load i8, i8* %"struct Foo.c", align 1 %10 = sext i8 %9 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct Foo.c") %11 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %10, i64* %11, align 8 %12 = load i64, i64* %"$foo", align 8 %13 = add i64 %12, 8 %14 = bitcast i64* %"struct Foo.l" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.l", i32 8, i64 %13) %15 = load i64, i64* %"struct Foo.l", align 8 %16 = bitcast i64* %"struct Foo.l" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) %17 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 2 store i64 %15, i64* %17, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 24) %18 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_reg.ll000066400000000000000000000027131413460502400212050ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 16 %reg_ip = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %reg_ip, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_signal.ll000066400000000000000000000010361413460502400217020ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = trunc i64 %arg0 to i32 %signal = call i64 inttoptr (i64 109 to i64 (i32)*)(i32 %3) ret i64 0 } attributes #0 = { nounwind } bpftrace-0.14.0/tests/codegen/llvm/call_signal_literal.ll000066400000000000000000000006111413460502400234140ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %signal = call i64 inttoptr (i64 109 to i64 (i32)*)(i32 8) ret i64 0 } attributes #0 = { nounwind } bpftrace-0.14.0/tests/codegen/llvm/call_signal_string_literal.ll000066400000000000000000000006111413460502400250020ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %signal = call i64 inttoptr (i64 109 to i64 (i32)*)(i32 9) ret i64 0 } attributes #0 = { nounwind } bpftrace-0.14.0/tests/codegen/llvm/call_sizeof.ll000066400000000000000000000025051413460502400217260ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_stats.ll000066400000000000000000000102371413460502400215660ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val8 = alloca i64, align 8 %"@x_key2" = alloca i64, align 8 %"@x_num" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_num" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = add i64 %4, 1 store i64 %7, i64* %"@x_num", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_num", i64 0) %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@x_num" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key2" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 1, i64* %"@x_key2", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem4 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo3, i64* %"@x_key2") %11 = bitcast i64* %lookup_elem_val8 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) %map_lookup_cond9 = icmp ne i8* %lookup_elem4, null br i1 %map_lookup_cond9, label %lookup_success5, label %lookup_failure6 lookup_success5: ; preds = %lookup_merge %cast10 = bitcast i8* %lookup_elem4 to i64* %12 = load i64, i64* %cast10, align 8 store i64 %12, i64* %lookup_elem_val8, align 8 br label %lookup_merge7 lookup_failure6: ; preds = %lookup_merge store i64 0, i64* %lookup_elem_val8, align 8 br label %lookup_merge7 lookup_merge7: ; preds = %lookup_failure6, %lookup_success5 %13 = load i64, i64* %lookup_elem_val8, align 8 %14 = bitcast i64* %lookup_elem_val8 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %16 = lshr i64 %get_pid_tgid, 32 %17 = add i64 %16, %13 store i64 %17, i64* %"@x_val", align 8 %pseudo11 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem12 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo11, i64* %"@x_key2", i64* %"@x_val", i64 0) %18 = bitcast i64* %"@x_key2" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) %19 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_str.ll000066400000000000000000000044561413460502400212460ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %str = alloca [64 x i8], align 1 %strlen = alloca i64, align 8 %1 = bitcast i64* %strlen to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast i64* %strlen to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 8, i1 false) store i64 64, i64* %strlen, align 8 %3 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %4 = bitcast [64 x i8]* %str to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 64, i1 false) %5 = bitcast i8* %0 to i64* %6 = getelementptr i64, i64* %5, i64 14 %arg0 = load volatile i64, i64* %6, align 8 %7 = load i64, i64* %strlen, align 8 %8 = trunc i64 %7 to i32 %probe_read_kernel_str = call i64 inttoptr (i64 115 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %str, i32 %8, i64 %arg0) %9 = bitcast i64* %strlen to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %str, i64 0) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_str_2_expr.ll000066400000000000000000000050571413460502400225230ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %str = alloca [64 x i8], align 1 %strlen = alloca i64, align 8 %1 = bitcast i64* %strlen to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast i64* %strlen to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 8, i1 false) %3 = bitcast i8* %0 to i64* %4 = getelementptr i64, i64* %3, i64 13 %arg1 = load volatile i64, i64* %4, align 8 %5 = add i64 %arg1, 1 %str.min.cmp = icmp ule i64 %5, 64 %str.min.select = select i1 %str.min.cmp, i64 %5, i64 64 store i64 %str.min.select, i64* %strlen, align 8 %6 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = bitcast [64 x i8]* %str to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %7, i8 0, i64 64, i1 false) %8 = bitcast i8* %0 to i64* %9 = getelementptr i64, i64* %8, i64 14 %arg0 = load volatile i64, i64* %9, align 8 %10 = load i64, i64* %strlen, align 8 %11 = trunc i64 %10 to i32 %probe_read_kernel_str = call i64 inttoptr (i64 115 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %str, i32 %11, i64 %arg0) %12 = bitcast i64* %strlen to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %str, i64 0) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) %15 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_str_2_lit.ll000066400000000000000000000044551413460502400223360ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %str = alloca [64 x i8], align 1 %strlen = alloca i64, align 8 %1 = bitcast i64* %strlen to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast i64* %strlen to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 8, i1 false) store i64 7, i64* %strlen, align 8 %3 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %4 = bitcast [64 x i8]* %str to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 64, i1 false) %5 = bitcast i8* %0 to i64* %6 = getelementptr i64, i64* %5, i64 14 %arg0 = load volatile i64, i64* %6, align 8 %7 = load i64, i64* %strlen, align 8 %8 = trunc i64 %7 to i32 %probe_read_kernel_str = call i64 inttoptr (i64 115 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %str, i32 %8, i64 %arg0) %9 = bitcast i64* %strlen to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %str, i64 0) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_strftime.ll000066400000000000000000000047511413460502400222710ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %strftime_t = type <{ i64, i64 }> %printf_t = type { i64, [16 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %strftime_args = alloca %strftime_t, align 8 %printf_args = alloca %printf_t, align 8 %1 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 24, i1 false) %3 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %3, align 8 %4 = bitcast %strftime_t* %strftime_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = getelementptr %strftime_t, %strftime_t* %strftime_args, i64 0, i32 0 store i64 0, i64* %5, align 8 %get_ns = call i64 inttoptr (i64 125 to i64 ()*)() %6 = getelementptr %strftime_t, %strftime_t* %strftime_args, i64 0, i32 1 store i64 %get_ns, i64* %6, align 8 %7 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 %8 = bitcast [16 x i8]* %7 to i8* %9 = bitcast %strftime_t* %strftime_args to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %8, i8* align 1 %9, i64 16, i1 false) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 24) %10 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_sum.ll000066400000000000000000000046571413460502400212450ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %7 = lshr i64 %get_pid_tgid, 32 %8 = add i64 %7, %4 store i64 %8, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_system.ll000066400000000000000000000033041413460502400217510ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %system_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %system_args = alloca %system_t, align 8 %1 = bitcast %system_t* %system_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %system_t* %system_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %3 = getelementptr %system_t, %system_t* %system_args, i32 0, i32 0 store i64 10000, i64* %3, align 8 %4 = getelementptr %system_t, %system_t* %system_args, i32 0, i32 1 store i64 100, i64* %4, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %system_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %system_t* %system_args, i64 16) %5 = bitcast %system_t* %system_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/call_time.ll000066400000000000000000000024461413460502400213710ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %time_t = type <{ i64, i32 }> ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %time_t = alloca %time_t, align 8 %1 = bitcast %time_t* %time_t to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %time_t, %time_t* %time_t, i64 0, i32 0 store i64 30004, i64* %2, align 8 %3 = getelementptr %time_t, %time_t* %time_t, i64 0, i32 1 store i32 0, i32* %3, align 4 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %time_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %time_t* %time_t, i64 12) %4 = bitcast %time_t* %time_t to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_uptr_1.ll000066400000000000000000000034621413460502400216440ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %deref = alloca i16, align 2 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast i16* %deref to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %probe_read_user = call i64 inttoptr (i64 112 to i64 (i16*, i32, i64)*)(i16* %deref, i32 2, i64 %arg0) %4 = load i16, i16* %deref, align 2 %5 = sext i16 %4 to i64 %6 = bitcast i16* %deref to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@_key", align 8 %8 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %5, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %9 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_uptr_2.ll000066400000000000000000000034621413460502400216450ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %deref = alloca i32, align 4 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast i32* %deref to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %probe_read_user = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %deref, i32 4, i64 %arg0) %4 = load i32, i32* %deref, align 4 %5 = sext i32 %4 to i64 %6 = bitcast i32* %deref to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@_key", align 8 %8 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %5, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %9 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_ustack.ll000066400000000000000000000050541413460502400217230ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 3) %get_stackid = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo, i64 256) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = shl i64 %get_pid_tgid, 32 %2 = or i64 %get_stackid, %1 %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@x_key", align 8 %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 %2, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 2) %get_stackid3 = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo2, i64 256) %get_pid_tgid4 = call i64 inttoptr (i64 14 to i64 ()*)() %7 = shl i64 %get_pid_tgid4, 32 %8 = or i64 %get_stackid3, %7 %9 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@y_key", align 8 %10 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 %8, i64* %"@y_val", align 8 %pseudo5 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem6 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo5, i64* %"@y_key", i64* %"@y_val", i64 0) %11 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_usym_key.ll000066400000000000000000000051471413460502400223010ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %usym_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %usym = alloca %usym_t, align 8 %1 = bitcast %usym_t* %usym to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %2 = lshr i64 %get_pid_tgid, 32 %3 = getelementptr %usym_t, %usym_t* %usym, i64 0, i32 0 %4 = getelementptr %usym_t, %usym_t* %usym, i64 0, i32 1 store i64 0, i64* %3, align 8 store i64 %2, i64* %4, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, %usym_t*)*)(i64 %pseudo, %usym_t* %usym) %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %6 = load i64, i64* %cast, align 8 store i64 %6, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %7 = load i64, i64* %lookup_elem_val, align 8 %8 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %10 = add i64 %7, 1 store i64 %10, i64* %"@x_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, %usym_t*, i64*, i64)*)(i64 %pseudo1, %usym_t* %usym, i64* %"@x_val", i64 0) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast %usym_t* %usym to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/call_zero.ll000066400000000000000000000040521413460502400214050ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %zero_t = type <{ i64, i32 }> ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"zero_@x" = alloca %zero_t, align 8 %1 = bitcast %zero_t* %"zero_@x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr %zero_t, %zero_t* %"zero_@x", i64 0, i32 0 store i64 30003, i64* %2, align 8 %3 = getelementptr %zero_t, %zero_t* %"zero_@x", i64 0, i32 1 store i32 0, i32* %3, align 4 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %zero_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %zero_t* %"zero_@x", i64 12) %4 = bitcast %zero_t* %"zero_@x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/comparison_extend.ll000066400000000000000000000027601413460502400231600ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = icmp ult i64 1, %arg0 %4 = zext i1 %3 to i64 %5 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@_key", align 8 %6 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) store i64 %4, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %7 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/dereference.ll000066400000000000000000000032531413460502400217040ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %deref = alloca i64, align 8 %1 = bitcast i64* %deref to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %deref, i32 8, i64 1234) %2 = load i64, i64* %deref, align 8 %3 = bitcast i64* %deref to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 0, i64* %"@x_key", align 8 %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %2, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %6 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/empty_function.ll000066400000000000000000000005141413460502400224750ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: ret i64 0 } attributes #0 = { nounwind } bpftrace-0.14.0/tests/codegen/llvm/enum_declaration.ll000066400000000000000000000037761413460502400227600ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@b_val" = alloca i64, align 8 %"@b_key" = alloca i64, align 8 %"@a_val" = alloca i64, align 8 %"@a_key" = alloca i64, align 8 %1 = bitcast i64* %"@a_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@a_key", align 8 %2 = bitcast i64* %"@a_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 42, i64* %"@a_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@a_key", i64* %"@a_val", i64 0) %3 = bitcast i64* %"@a_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@a_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@b_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@b_key", align 8 %6 = bitcast i64* %"@b_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) store i64 43, i64* %"@b_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem2 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@b_key", i64* %"@b_val", i64 0) %7 = bitcast i64* %"@b_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@b_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/if_else_printf.ll000066400000000000000000000054211413460502400224240ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t.0 = type { i64 } %printf_t = type { i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %printf_args1 = alloca %printf_t.0, align 8 %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %2 = icmp ugt i64 %1, 10 %3 = zext i1 %2 to i64 %true_cond = icmp ne i64 %3, 0 br i1 %true_cond, label %if_body, label %else_body if_body: ; preds = %entry %4 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 8, i1 false) %6 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %6, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 8) %7 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) br label %if_end if_end: ; preds = %else_body, %if_body ret i64 0 else_body: ; preds = %entry %8 = bitcast %printf_t.0* %printf_args1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) %9 = bitcast %printf_t.0* %printf_args1 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %9, i8 0, i64 8, i1 false) %10 = getelementptr %printf_t.0, %printf_t.0* %printf_args1, i32 0, i32 0 store i64 1, i64* %10, align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output3 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.0*, i64)*)(i8* %0, i64 %pseudo2, i64 4294967295, %printf_t.0* %printf_args1, i64 8) %11 = bitcast %printf_t.0* %printf_args1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) br label %if_end } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/if_else_variable.ll000066400000000000000000000046571413460502400227210ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %printf_args = alloca %printf_t, align 8 %"$s" = alloca i64, align 8 %1 = bitcast i64* %"$s" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$s", align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %2 = lshr i64 %get_pid_tgid, 32 %3 = icmp ugt i64 %2, 10000 %4 = zext i1 %3 to i64 %true_cond = icmp ne i64 %4, 0 br i1 %true_cond, label %if_body, label %else_body if_body: ; preds = %entry store i64 10, i64* %"$s", align 8 br label %if_end if_end: ; preds = %else_body, %if_body %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) %6 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %6, i8 0, i64 16, i1 false) %7 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %7, align 8 %8 = load i64, i64* %"$s", align 8 %9 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %8, i64* %9, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 16) %10 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 else_body: ; preds = %entry store i64 20, i64* %"$s", align 8 br label %if_end } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/if_nested_printf.ll000066400000000000000000000046101413460502400227550ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %2 = icmp ugt i64 %1, 10000 %3 = zext i1 %2 to i64 %true_cond = icmp ne i64 %3, 0 br i1 %true_cond, label %if_body, label %if_end if_body: ; preds = %entry %get_pid_tgid3 = call i64 inttoptr (i64 14 to i64 ()*)() %4 = lshr i64 %get_pid_tgid3, 32 %5 = urem i64 %4, 2 %6 = icmp eq i64 %5, 0 %7 = zext i1 %6 to i64 %true_cond4 = icmp ne i64 %7, 0 br i1 %true_cond4, label %if_body1, label %if_end2 if_end: ; preds = %if_end2, %entry ret i64 0 if_body1: ; preds = %if_body %8 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) %9 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %9, i8 0, i64 8, i1 false) %10 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %10, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 8) %11 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) br label %if_end2 if_end2: ; preds = %if_body1, %if_body br label %if_end } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/if_printf.ll000066400000000000000000000042501413460502400214130ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %2 = icmp ugt i64 %1, 10000 %3 = zext i1 %2 to i64 %true_cond = icmp ne i64 %3, 0 br i1 %true_cond, label %if_body, label %if_end if_body: ; preds = %entry %4 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 16, i1 false) %6 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %6, align 8 %get_pid_tgid1 = call i64 inttoptr (i64 14 to i64 ()*)() %7 = lshr i64 %get_pid_tgid1, 32 %8 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %7, i64* %8, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 16) %9 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) br label %if_end if_end: ; preds = %if_body, %entry ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/if_variable.ll000066400000000000000000000044551413460502400217050ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %printf_args = alloca %printf_t, align 8 %"$s" = alloca i64, align 8 %1 = bitcast i64* %"$s" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$s", align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %2 = lshr i64 %get_pid_tgid, 32 %3 = icmp ugt i64 %2, 10000 %4 = zext i1 %3 to i64 %true_cond = icmp ne i64 %4, 0 br i1 %true_cond, label %if_body, label %if_end if_body: ; preds = %entry store i64 10, i64* %"$s", align 8 br label %if_end if_end: ; preds = %if_body, %entry %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) %6 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %6, i8 0, i64 16, i1 false) %7 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %7, align 8 %8 = load i64, i64* %"$s", align 8 %9 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %8, i64* %9, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 16) %10 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/int_propagation.ll000066400000000000000000000063711413460502400226360ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 1234, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@x_key1") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@y_key", align 8 %12 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) store i64 %8, i64* %"@y_val", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo3, i64* %"@y_key", i64* %"@y_val", i64 0) %13 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/intcast_call.ll000066400000000000000000000050141413460502400220720ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:f"(i8* %0) section "s_kretprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = bitcast i8* %0 to i64* %8 = getelementptr i64, i64* %7, i64 10 %retval = load volatile i64, i64* %8, align 8 %cast1 = trunc i64 %retval to i32 %9 = sext i32 %cast1 to i64 %10 = add i64 %9, %4 store i64 %10, i64* %"@_val", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@_key", i64* %"@_val", i64 0) %11 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/intcast_retval.ll000066400000000000000000000030021413460502400224470ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:f"(i8* %0) section "s_kretprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 10 %retval = load volatile i64, i64* %2, align 8 %cast = trunc i64 %retval to i32 %3 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@_key", align 8 %4 = sext i32 %cast to i64 %5 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 %4, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %6 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/intptrcast_assign_var.ll000066400000000000000000000034161413460502400240450ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:f"(i8* %0) section "s_kretprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %deref = alloca i8, align 1 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 4 %reg_bp = load volatile i64, i64* %2, align 8 %3 = sub i64 %reg_bp, 1 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %deref) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %deref, i32 1, i64 %3) %4 = load i8, i8* %deref, align 1 %5 = sext i8 %4 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %deref) %6 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) store i64 0, i64* %"@_key", align 8 %7 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 %5, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %8 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/intptrcast_call.ll000066400000000000000000000054321413460502400226240ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:f"(i8* %0) section "s_kretprobe:f_1" { entry: %deref = alloca i8, align 1 %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = bitcast i8* %0 to i64* %8 = getelementptr i64, i64* %7, i64 4 %reg_bp = load volatile i64, i64* %8, align 8 %9 = sub i64 %reg_bp, 1 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %deref) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %deref, i32 1, i64 %9) %10 = load i8, i8* %deref, align 1 %11 = sext i8 %10 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %deref) %12 = add i64 %11, %4 store i64 %12, i64* %"@_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_val", i64 0) %13 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/literal_strncmp.ll000066400000000000000000000120021413460502400226270ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:vfs_read"(i8* %0) section "s_kretprobe:vfs_read_1" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %comm5 = alloca [16 x i8], align 1 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 %1 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [16 x i8]* %comm to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm, i64 16) %3 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i1 true, i1* %strcmp.result, align 1 %4 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 0 %5 = load i8, i8* %4, align 1 %strcmp.cmp = icmp ne i8 %5, 115 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp pred_false: ; preds = %strcmp.false ret i64 0 pred_true: ; preds = %strcmp.false %6 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast [16 x i8]* %comm5 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %8 = bitcast [16 x i8]* %comm5 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 16, i1 false) %get_comm6 = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm5, i64 16) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, [16 x i8]*)*)(i64 %pseudo, [16 x i8]* %comm5) %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure strcmp.false: ; preds = %strcmp.done, %strcmp.loop, %entry %10 = load i1, i1* %strcmp.result, align 1 %11 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = zext i1 %10 to i64 %predcond = icmp eq i64 %12, 0 br i1 %predcond, label %pred_false, label %pred_true strcmp.done: ; preds = %strcmp.loop1, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 false, i1* %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %13 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 1 %14 = load i8, i8* %13, align 1 %strcmp.cmp3 = icmp ne i8 %14, 115 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %5, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 br label %strcmp.done strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %14, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 lookup_success: ; preds = %pred_true %cast = bitcast i8* %lookup_elem to i64* %15 = load i64, i64* %cast, align 8 store i64 %15, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %pred_true store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %16 = load i64, i64* %lookup_elem_val, align 8 %17 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %17) %18 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %18) %19 = add i64 %16, 1 store i64 %19, i64* %"@_val", align 8 %pseudo7 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [16 x i8]*, i64*, i64)*)(i64 %pseudo7, [16 x i8]* %comm5, i64* %"@_val", i64 0) %20 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) %21 = bitcast [16 x i8]* %comm5 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %21) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/logical_and.ll000066400000000000000000000046501413460502400216730ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"&&_result" = alloca i64, align 8 %1 = bitcast i64* %"&&_result" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %2 = lshr i64 %get_pid_tgid, 32 %3 = icmp ne i64 %2, 1234 %4 = zext i1 %3 to i64 %lhs_true_cond = icmp ne i64 %4, 0 br i1 %lhs_true_cond, label %"&&_lhs_true", label %"&&_false" "&&_lhs_true": ; preds = %entry %get_pid_tgid1 = call i64 inttoptr (i64 14 to i64 ()*)() %5 = lshr i64 %get_pid_tgid1, 32 %6 = icmp ne i64 %5, 1235 %7 = zext i1 %6 to i64 %rhs_true_cond = icmp ne i64 %7, 0 br i1 %rhs_true_cond, label %"&&_true", label %"&&_false" "&&_true": ; preds = %"&&_lhs_true" store i64 1, i64* %"&&_result", align 8 br label %"&&_merge" "&&_false": ; preds = %"&&_lhs_true", %entry store i64 0, i64* %"&&_result", align 8 br label %"&&_merge" "&&_merge": ; preds = %"&&_false", %"&&_true" %8 = load i64, i64* %"&&_result", align 8 %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/logical_and_or_different_type.ll000066400000000000000000000165441413460502400254670ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64, i64, i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"struct Foo.m16" = alloca i32, align 4 %"||_result15" = alloca i64, align 8 %"struct Foo.m8" = alloca i32, align 4 %"||_result" = alloca i64, align 8 %"struct Foo.m6" = alloca i32, align 4 %"&&_result5" = alloca i64, align 8 %"struct Foo.m" = alloca i32, align 4 %"&&_result" = alloca i64, align 8 %printf_args = alloca %printf_t, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 40, i1 false) %6 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %6, align 8 %7 = bitcast i64* %"&&_result" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %8 = load i64, i64* %"$foo", align 8 %9 = add i64 %8, 0 %10 = bitcast i32* %"struct Foo.m" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %probe_read_user = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m", i32 4, i64 %9) %11 = load i32, i32* %"struct Foo.m", align 4 %12 = sext i32 %11 to i64 %13 = bitcast i32* %"struct Foo.m" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %lhs_true_cond = icmp ne i64 %12, 0 br i1 %lhs_true_cond, label %"&&_lhs_true", label %"&&_false" "&&_lhs_true": ; preds = %entry br i1 false, label %"&&_true", label %"&&_false" "&&_true": ; preds = %"&&_lhs_true" store i64 1, i64* %"&&_result", align 8 br label %"&&_merge" "&&_false": ; preds = %"&&_lhs_true", %entry store i64 0, i64* %"&&_result", align 8 br label %"&&_merge" "&&_merge": ; preds = %"&&_false", %"&&_true" %14 = load i64, i64* %"&&_result", align 8 %15 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %14, i64* %15, align 8 %16 = bitcast i64* %"&&_result5" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) br i1 true, label %"&&_lhs_true1", label %"&&_false3" "&&_lhs_true1": ; preds = %"&&_merge" %17 = load i64, i64* %"$foo", align 8 %18 = add i64 %17, 0 %19 = bitcast i32* %"struct Foo.m6" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %19) %probe_read_user7 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m6", i32 4, i64 %18) %20 = load i32, i32* %"struct Foo.m6", align 4 %21 = sext i32 %20 to i64 %22 = bitcast i32* %"struct Foo.m6" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %22) %rhs_true_cond = icmp ne i64 %21, 0 br i1 %rhs_true_cond, label %"&&_true2", label %"&&_false3" "&&_true2": ; preds = %"&&_lhs_true1" store i64 1, i64* %"&&_result5", align 8 br label %"&&_merge4" "&&_false3": ; preds = %"&&_lhs_true1", %"&&_merge" store i64 0, i64* %"&&_result5", align 8 br label %"&&_merge4" "&&_merge4": ; preds = %"&&_false3", %"&&_true2" %23 = load i64, i64* %"&&_result5", align 8 %24 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 2 store i64 %23, i64* %24, align 8 %25 = bitcast i64* %"||_result" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %25) %26 = load i64, i64* %"$foo", align 8 %27 = add i64 %26, 0 %28 = bitcast i32* %"struct Foo.m8" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %28) %probe_read_user9 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m8", i32 4, i64 %27) %29 = load i32, i32* %"struct Foo.m8", align 4 %30 = sext i32 %29 to i64 %31 = bitcast i32* %"struct Foo.m8" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %31) %lhs_true_cond10 = icmp ne i64 %30, 0 br i1 %lhs_true_cond10, label %"||_true", label %"||_lhs_false" "||_lhs_false": ; preds = %"&&_merge4" br i1 false, label %"||_true", label %"||_false" "||_false": ; preds = %"||_lhs_false" store i64 0, i64* %"||_result", align 8 br label %"||_merge" "||_true": ; preds = %"||_lhs_false", %"&&_merge4" store i64 1, i64* %"||_result", align 8 br label %"||_merge" "||_merge": ; preds = %"||_true", %"||_false" %32 = load i64, i64* %"||_result", align 8 %33 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 3 store i64 %32, i64* %33, align 8 %34 = bitcast i64* %"||_result15" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %34) br i1 false, label %"||_true13", label %"||_lhs_false11" "||_lhs_false11": ; preds = %"||_merge" %35 = load i64, i64* %"$foo", align 8 %36 = add i64 %35, 0 %37 = bitcast i32* %"struct Foo.m16" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %37) %probe_read_user17 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m16", i32 4, i64 %36) %38 = load i32, i32* %"struct Foo.m16", align 4 %39 = sext i32 %38 to i64 %40 = bitcast i32* %"struct Foo.m16" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %40) %rhs_true_cond18 = icmp ne i64 %39, 0 br i1 %rhs_true_cond18, label %"||_true13", label %"||_false12" "||_false12": ; preds = %"||_lhs_false11" store i64 0, i64* %"||_result15", align 8 br label %"||_merge14" "||_true13": ; preds = %"||_lhs_false11", %"||_merge" store i64 1, i64* %"||_result15", align 8 br label %"||_merge14" "||_merge14": ; preds = %"||_true13", %"||_false12" %41 = load i64, i64* %"||_result15", align 8 %42 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 4 store i64 %41, i64* %42, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 40) %43 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %43) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/logical_not.ll000066400000000000000000000037641413460502400217360ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 0, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@y_key", align 8 %6 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) store i64 1, i64* %"@y_val", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem2 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@y_key", i64* %"@y_val", i64 0) %7 = bitcast i64* %"@y_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/logical_or.ll000066400000000000000000000046521413460502400215530ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"||_result" = alloca i64, align 8 %1 = bitcast i64* %"||_result" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %2 = lshr i64 %get_pid_tgid, 32 %3 = icmp eq i64 %2, 1234 %4 = zext i1 %3 to i64 %lhs_true_cond = icmp ne i64 %4, 0 br i1 %lhs_true_cond, label %"||_true", label %"||_lhs_false" "||_lhs_false": ; preds = %entry %get_pid_tgid1 = call i64 inttoptr (i64 14 to i64 ()*)() %5 = lshr i64 %get_pid_tgid1, 32 %6 = icmp eq i64 %5, 1235 %7 = zext i1 %6 to i64 %rhs_true_cond = icmp ne i64 %7, 0 br i1 %rhs_true_cond, label %"||_true", label %"||_false" "||_false": ; preds = %"||_lhs_false" store i64 0, i64* %"||_result", align 8 br label %"||_merge" "||_true": ; preds = %"||_lhs_false", %entry store i64 1, i64* %"||_result", align 8 br label %"||_merge" "||_merge": ; preds = %"||_true", %"||_false" %8 = load i64, i64* %"||_result", align 8 %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/macro_definition.ll000066400000000000000000000025051413460502400227450ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@_key", align 8 %2 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 100, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %3 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_assign_array.ll000066400000000000000000000072021413460502400227520ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"$var" = alloca i32, align 4 %1 = bitcast i32* %"$var" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i32 0, i32* %"$var", align 4 %lookup_elem_val = alloca [4 x i32], align 4 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca [4 x i32], align 4 %"@x_key" = alloca i64, align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 %4 = add i64 %arg0, 0 %5 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key", align 8 %6 = bitcast [4 x i32]* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([4 x i32]*, i32, i64)*)([4 x i32]* %"@x_val", i32 16, i64 %4) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [4 x i32]*, i64)*)(i64 %pseudo, i64* %"@x_key", [4 x i32]* %"@x_val", i64 0) %7 = bitcast [4 x i32]* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@x_key1") %10 = bitcast [4 x i32]* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %11 = bitcast [4 x i32]* %lookup_elem_val to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %11, i8* align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry %12 = bitcast [4 x i32]* %lookup_elem_val to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %12, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %13 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = getelementptr [4 x i32], [4 x i32]* %lookup_elem_val, i32 0, i64 0 %15 = load volatile i32, i32* %14, align 4 %16 = bitcast [4 x i32]* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) store i32 %15, i32* %"$var", align 4 ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/map_assign_int.ll000066400000000000000000000025151413460502400224300ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_assign_string.ll000066400000000000000000000030301413460502400231350ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %str = alloca [64 x i8], align 1 %1 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store [64 x i8] c"blah\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %str, i64 0) %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_increment_decrement.ll000066400000000000000000000207221413460502400243040ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@x_newval38" = alloca i64, align 8 %lookup_elem_val35 = alloca i64, align 8 %"@x_key29" = alloca i64, align 8 %"@x_newval26" = alloca i64, align 8 %lookup_elem_val23 = alloca i64, align 8 %"@x_key17" = alloca i64, align 8 %"@x_newval14" = alloca i64, align 8 %lookup_elem_val11 = alloca i64, align 8 %"@x_key5" = alloca i64, align 8 %"@x_newval" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 10, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@x_key1") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_newval" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 store i64 %11, i64* %"@x_newval", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo3, i64* %"@x_key1", i64* %"@x_newval", i64 0) %12 = bitcast i64* %"@x_newval" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key5" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 0, i64* %"@x_key5", align 8 %pseudo6 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem7 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo6, i64* %"@x_key5") %15 = bitcast i64* %lookup_elem_val11 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) %map_lookup_cond12 = icmp ne i8* %lookup_elem7, null br i1 %map_lookup_cond12, label %lookup_success8, label %lookup_failure9 lookup_success8: ; preds = %lookup_merge %cast13 = bitcast i8* %lookup_elem7 to i64* %16 = load i64, i64* %cast13, align 8 store i64 %16, i64* %lookup_elem_val11, align 8 br label %lookup_merge10 lookup_failure9: ; preds = %lookup_merge store i64 0, i64* %lookup_elem_val11, align 8 br label %lookup_merge10 lookup_merge10: ; preds = %lookup_failure9, %lookup_success8 %17 = load i64, i64* %lookup_elem_val11, align 8 %18 = bitcast i64* %lookup_elem_val11 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) %19 = bitcast i64* %"@x_newval14" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %19) %20 = add i64 %17, 1 store i64 %20, i64* %"@x_newval14", align 8 %pseudo15 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem16 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo15, i64* %"@x_key5", i64* %"@x_newval14", i64 0) %21 = load i64, i64* %"@x_newval14", align 8 %22 = bitcast i64* %"@x_newval14" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %22) %23 = bitcast i64* %"@x_key5" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %23) %24 = bitcast i64* %"@x_key17" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %24) store i64 0, i64* %"@x_key17", align 8 %pseudo18 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem19 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo18, i64* %"@x_key17") %25 = bitcast i64* %lookup_elem_val23 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %25) %map_lookup_cond24 = icmp ne i8* %lookup_elem19, null br i1 %map_lookup_cond24, label %lookup_success20, label %lookup_failure21 lookup_success20: ; preds = %lookup_merge10 %cast25 = bitcast i8* %lookup_elem19 to i64* %26 = load i64, i64* %cast25, align 8 store i64 %26, i64* %lookup_elem_val23, align 8 br label %lookup_merge22 lookup_failure21: ; preds = %lookup_merge10 store i64 0, i64* %lookup_elem_val23, align 8 br label %lookup_merge22 lookup_merge22: ; preds = %lookup_failure21, %lookup_success20 %27 = load i64, i64* %lookup_elem_val23, align 8 %28 = bitcast i64* %lookup_elem_val23 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %28) %29 = bitcast i64* %"@x_newval26" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %29) %30 = sub i64 %27, 1 store i64 %30, i64* %"@x_newval26", align 8 %pseudo27 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem28 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo27, i64* %"@x_key17", i64* %"@x_newval26", i64 0) %31 = bitcast i64* %"@x_newval26" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %31) %32 = bitcast i64* %"@x_key17" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %32) %33 = bitcast i64* %"@x_key29" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %33) store i64 0, i64* %"@x_key29", align 8 %pseudo30 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem31 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo30, i64* %"@x_key29") %34 = bitcast i64* %lookup_elem_val35 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %34) %map_lookup_cond36 = icmp ne i8* %lookup_elem31, null br i1 %map_lookup_cond36, label %lookup_success32, label %lookup_failure33 lookup_success32: ; preds = %lookup_merge22 %cast37 = bitcast i8* %lookup_elem31 to i64* %35 = load i64, i64* %cast37, align 8 store i64 %35, i64* %lookup_elem_val35, align 8 br label %lookup_merge34 lookup_failure33: ; preds = %lookup_merge22 store i64 0, i64* %lookup_elem_val35, align 8 br label %lookup_merge34 lookup_merge34: ; preds = %lookup_failure33, %lookup_success32 %36 = load i64, i64* %lookup_elem_val35, align 8 %37 = bitcast i64* %lookup_elem_val35 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %37) %38 = bitcast i64* %"@x_newval38" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %38) %39 = sub i64 %36, 1 store i64 %39, i64* %"@x_newval38", align 8 %pseudo39 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem40 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo39, i64* %"@x_key29", i64* %"@x_newval38", i64 0) %40 = load i64, i64* %"@x_newval38", align 8 %41 = bitcast i64* %"@x_newval38" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %41) %42 = bitcast i64* %"@x_key29" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %42) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_key_array.ll000066400000000000000000000031131413460502400222530ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca [4 x i32], align 4 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = add i64 %arg0, 0 %4 = bitcast [4 x i32]* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([4 x i32]*, i32, i64)*)([4 x i32]* %"@x_key", i32 16, i64 %3) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 44, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [4 x i32]*, i64*, i64)*)(i64 %pseudo, [4 x i32]* %"@x_key", i64* %"@x_val", i64 0) %6 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast [4 x i32]* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_key_int.ll000066400000000000000000000033161413460502400217340ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca [24 x i8], align 1 %1 = bitcast [24 x i8]* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = getelementptr [24 x i8], [24 x i8]* %"@x_key", i64 0, i64 0 %3 = bitcast i8* %2 to i64* store i64 11, i64* %3, align 8 %4 = getelementptr [24 x i8], [24 x i8]* %"@x_key", i64 0, i64 8 %5 = bitcast i8* %4 to i64* store i64 22, i64* %5, align 8 %6 = getelementptr [24 x i8], [24 x i8]* %"@x_key", i64 0, i64 16 %7 = bitcast i8* %6 to i64* store i64 33, i64* %7, align 8 %8 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 44, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [24 x i8]*, i64*, i64)*)(i64 %pseudo, [24 x i8]* %"@x_key", i64* %"@x_val", i64 0) %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast [24 x i8]* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_key_probe.ll000066400000000000000000000112411413460502400222450ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:sched:sched_one"(i8* %0) section "s_tracepoint:sched:sched_one_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = add i64 %4, 1 %8 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 0, i64* %"@x_key1", align 8 %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 %7, i64* %"@x_val", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@x_key1", i64* %"@x_val", i64 0) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 define i64 @"tracepoint:sched:sched_two"(i8* %0) section "s_tracepoint:sched:sched_two_2" { entry: %"@x_val" = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 1, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@x_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = add i64 %4, 1 %8 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 1, i64* %"@x_key1", align 8 %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 %7, i64* %"@x_val", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@x_key1", i64* %"@x_val", i64 0) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 1 } attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_key_string.ll000066400000000000000000000054641413460502400224560ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %str1 = alloca [64 x i8], align 1 %str = alloca [64 x i8], align 1 %"@x_key" = alloca [128 x i8], align 1 %1 = bitcast [128 x i8]* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store [64 x i8] c"a\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 %3 = getelementptr [128 x i8], [128 x i8]* %"@x_key", i64 0, i64 0 %4 = bitcast [64 x i8]* %str to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %3, i8* align 1 %4, i64 64, i1 false) %5 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast [64 x i8]* %str1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) store [64 x i8] c"b\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str1, align 1 %7 = getelementptr [128 x i8], [128 x i8]* %"@x_key", i64 0, i64 64 %8 = bitcast [64 x i8]* %str1 to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 64, i1 false) %9 = bitcast [64 x i8]* %str1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 44, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [128 x i8]*, i64*, i64)*)(i64 %pseudo, [128 x i8]* %"@x_key", i64* %"@x_val", i64 0) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast [128 x i8]* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/map_key_struct.ll000066400000000000000000000030561413460502400224670ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca [4 x i8], align 1 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast [4 x i8]* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([4 x i8]*, i32, i64)*)([4 x i8]* %"@x_key", i32 4, i64 %arg0) %4 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 44, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [4 x i8]*, i64*, i64)*)(i64 %pseudo, [4 x i8]* %"@x_key", i64* %"@x_val", i64 0) %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast [4 x i8]* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/multiple_identical_probes.ll000066400000000000000000000006341413460502400246560ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: ret i64 0 } define i64 @"kprobe:f.1"(i8* %0) section "s_kprobe:f_2" { entry: ret i64 0 } attributes #0 = { nounwind } bpftrace-0.14.0/tests/codegen/llvm/nested_array_struct.ll000066400000000000000000000107441413460502400235240ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %lookup_elem_val = alloca [2 x [2 x [4 x i8]]], align 1 %"@bar_key1" = alloca i64, align 8 %"@bar_val" = alloca [2 x [2 x [4 x i8]]], align 1 %"@bar_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = add i64 %arg0, 0 %4 = bitcast i64* %"@bar_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 42, i64* %"@bar_key", align 8 %5 = bitcast [2 x [2 x [4 x i8]]]* %"@bar_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([2 x [2 x [4 x i8]]]*, i32, i64)*)([2 x [2 x [4 x i8]]]* %"@bar_val", i32 16, i64 %3) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [2 x [2 x [4 x i8]]]*, i64)*)(i64 %pseudo, i64* %"@bar_key", [2 x [2 x [4 x i8]]]* %"@bar_val", i64 0) %6 = bitcast [2 x [2 x [4 x i8]]]* %"@bar_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@bar_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@bar_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 42, i64* %"@bar_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@bar_key1") %9 = bitcast [2 x [2 x [4 x i8]]]* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %10 = bitcast [2 x [2 x [4 x i8]]]* %lookup_elem_val to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %10, i8* align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry %11 = bitcast [2 x [2 x [4 x i8]]]* %lookup_elem_val to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %11, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %12 = bitcast i64* %"@bar_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = getelementptr [2 x [2 x [4 x i8]]], [2 x [2 x [4 x i8]]]* %lookup_elem_val, i32 0, i64 0 %14 = getelementptr [2 x [4 x i8]], [2 x [4 x i8]]* %13, i32 0, i64 1 %15 = getelementptr [4 x i8], [4 x i8]* %14, i32 0, i64 0 %16 = bitcast i8* %15 to i32* %17 = load volatile i32, i32* %16, align 4 %18 = bitcast [2 x [2 x [4 x i8]]]* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) %19 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %19) store i64 0, i64* %"@_key", align 8 %20 = sext i32 %17 to i64 %21 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %21) store i64 %20, i64* %"@_val", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo3, i64* %"@_key", i64* %"@_val", i64 0) %22 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %22) %23 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %23) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/nested_while_loop.ll000066400000000000000000000076441413460502400231500ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"interval:s:1"(i8* %0) section "s_interval:s:1_1" { entry: %"@_newval" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %"$j" = alloca i64, align 8 %1 = bitcast i64* %"$j" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$j", align 8 %"$i" = alloca i64, align 8 %2 = bitcast i64* %"$i" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"$i", align 8 store i64 1, i64* %"$i", align 8 br label %while_cond while_cond: ; preds = %while_end3, %entry %3 = load i64, i64* %"$i", align 8 %4 = icmp sle i64 %3, 100 %5 = zext i1 %4 to i64 %true_cond = icmp ne i64 %5, 0 br i1 %true_cond, label %while_body, label %while_end, !llvm.loop !0 while_body: ; preds = %while_cond store i64 0, i64* %"$j", align 8 %6 = load i64, i64* %"$i", align 8 %7 = add i64 %6, 1 store i64 %7, i64* %"$i", align 8 br label %while_cond1 while_end: ; preds = %while_cond ret i64 0 while_cond1: ; preds = %lookup_merge, %while_body %8 = load i64, i64* %"$j", align 8 %9 = icmp sle i64 %8, 100 %10 = zext i1 %9 to i64 %true_cond4 = icmp ne i64 %10, 0 br i1 %true_cond4, label %while_body2, label %while_end3, !llvm.loop !0 while_body2: ; preds = %while_cond1 %11 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %12 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end3: ; preds = %while_cond1 br label %while_cond lookup_success: ; preds = %while_body2 %cast = bitcast i8* %lookup_elem to i64* %13 = load i64, i64* %cast, align 8 store i64 %13, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %while_body2 store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %14 = load i64, i64* %lookup_elem_val, align 8 %15 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) %17 = add i64 %14, 1 store i64 %17, i64* %"@_newval", align 8 %pseudo5 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo5, i64* %"@_key", i64* %"@_newval", i64 0) %18 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) %19 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) %20 = load i64, i64* %"$j", align 8 %21 = add i64 %20, 1 store i64 %21, i64* %"$j", align 8 br label %while_cond1 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } !0 = distinct !{!0, !1} !1 = !{!"llvm.loop.unroll.disable"} bpftrace-0.14.0/tests/codegen/llvm/optional_positional_parameter.ll000066400000000000000000000064221413460502400255640ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@y_key" = alloca i64, align 8 %str1 = alloca [1 x i8], align 1 %str = alloca [64 x i8], align 1 %strlen = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %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* %2) store i64 0, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %3 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %strlen to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %strlen to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %6, i8 0, i64 8, i1 false) store i64 64, i64* %strlen, align 8 %7 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %8 = bitcast [64 x i8]* %str to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 64, i1 false) %9 = bitcast [1 x i8]* %str1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %10 = bitcast [1 x i8]* %str1 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %10, i8 0, i64 1, i1 false) store [1 x i8] zeroinitializer, [1 x i8]* %str1, align 1 %11 = ptrtoint [1 x i8]* %str1 to i64 %12 = load i64, i64* %strlen, align 8 %13 = trunc i64 %12 to i32 %probe_read_kernel_str = call i64 inttoptr (i64 115 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %str, i32 %13, i64 %11) %14 = bitcast i64* %strlen to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) %15 = bitcast [1 x i8]* %str1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) store i64 0, i64* %"@y_key", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem3 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo2, i64* %"@y_key", [64 x i8]* %str, i64 0) %17 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %17) %18 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/pointer_add_int.ll000066400000000000000000000014651413460502400226020ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"$v" = alloca i64, align 8 %1 = bitcast i64* %"$v" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$v", align 8 store i64 1000, i64* %"$v", align 8 %2 = load i64, i64* %"$v", align 8 %3 = add i64 %2, 20 store i64 %3, i64* %"$v", align 8 ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/pointer_inc_map.ll000066400000000000000000000104501413460502400226000ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@_newval" = alloca i64, align 8 %lookup_elem_val9 = alloca i64, align 8 %"@_key3" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key1" = alloca i64, align 8 %"@_ptr" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@_key", align 8 %2 = bitcast i64* %"@_ptr" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 1000, i64* %"@_ptr", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_ptr", i64 0) %3 = bitcast i64* %"@_ptr" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@_key1") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@_key3" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@_key3", align 8 %pseudo4 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem5 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo4, i64* %"@_key3") %12 = bitcast i64* %lookup_elem_val9 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) %map_lookup_cond10 = icmp ne i8* %lookup_elem5, null br i1 %map_lookup_cond10, label %lookup_success6, label %lookup_failure7 lookup_success6: ; preds = %lookup_merge %cast11 = bitcast i8* %lookup_elem5 to i64* %13 = load i64, i64* %cast11, align 8 store i64 %13, i64* %lookup_elem_val9, align 8 br label %lookup_merge8 lookup_failure7: ; preds = %lookup_merge store i64 0, i64* %lookup_elem_val9, align 8 br label %lookup_merge8 lookup_merge8: ; preds = %lookup_failure7, %lookup_success6 %14 = load i64, i64* %lookup_elem_val9, align 8 %15 = bitcast i64* %lookup_elem_val9 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) %17 = add i64 %14, 2 store i64 %17, i64* %"@_newval", align 8 %pseudo12 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem13 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo12, i64* %"@_key3", i64* %"@_newval", i64 0) %18 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) %19 = bitcast i64* %"@_key3" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/pointer_inc_var.ll000066400000000000000000000015311413460502400226130ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"$v" = alloca i64, align 8 %1 = bitcast i64* %"$v" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$v", align 8 store i64 1000, i64* %"$v", align 8 %2 = load i64, i64* %"$v", align 8 %3 = load i64, i64* %"$v", align 8 %4 = add i64 %3, 2 store i64 %4, i64* %"$v", align 8 ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/pred_binop.ll000066400000000000000000000033111413460502400215510ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %2 = icmp eq i64 %1, 1234 %3 = zext i1 %2 to i64 %predcond = icmp eq i64 %3, 0 br i1 %predcond, label %pred_false, label %pred_true pred_false: ; preds = %entry ret i64 0 pred_true: ; preds = %entry %4 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) store i64 0, i64* %"@x_key", align 8 %5 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 1, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %6 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/ptr_to_ptr.ll000066400000000000000000000052041413460502400216270ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %deref1 = alloca i32, align 4 %deref = alloca i64, align 8 %printf_args = alloca %printf_t, align 8 %"$pp" = alloca i64, align 8 %1 = bitcast i64* %"$pp" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$pp", align 8 store i64 0, i64* %"$pp", align 8 %2 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %3 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %3, i8 0, i64 16, i1 false) %4 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %4, align 8 %5 = load i64, i64* %"$pp", align 8 %6 = bitcast i64* %deref to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %deref, i32 8, i64 %5) %7 = load i64, i64* %deref, align 8 %8 = bitcast i64* %deref to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i32* %deref1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %probe_read_kernel2 = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %deref1, i32 4, i64 %7) %10 = load i32, i32* %deref1, align 4 %11 = sext i32 %10 to i64 %12 = bitcast i32* %deref1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %11, i64* %13, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 16) %14 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/runtime_error_check.ll000066400000000000000000000070321413460502400234650ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %helper_error_t = type <{ i64, i64, i32 }> ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %helper_error_t = alloca %helper_error_t, align 8 %"@_newval" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %4 = load i64, i64* %lookup_elem_val, align 8 %5 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = add i64 %4, 1 store i64 %7, i64* %"@_newval", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo1, i64* %"@_key", i64* %"@_newval", i64 0) %8 = trunc i64 %update_elem to i32 %9 = icmp sge i32 %8, 0 br i1 %9, label %helper_merge, label %helper_failure helper_failure: ; preds = %lookup_merge %10 = bitcast %helper_error_t* %helper_error_t to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %11 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t, i64 0, i32 0 store i64 30006, i64* %11, align 8 %12 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t, i64 0, i32 1 store i64 0, i64* %12, align 8 %13 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t, i64 0, i32 2 store i32 %8, i32* %13, align 4 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %helper_error_t*, i64)*)(i8* %0, i64 %pseudo2, i64 4294967295, %helper_error_t* %helper_error_t, i64 20) %14 = bitcast %helper_error_t* %helper_error_t to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) br label %helper_merge helper_merge: ; preds = %helper_failure, %lookup_merge %15 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/runtime_error_check_lookup.ll000066400000000000000000000106021413460502400250530ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %helper_error_t = type <{ i64, i64, i32 }> ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %helper_error_t3 = alloca %helper_error_t, align 8 %"@_newval" = alloca i64, align 8 %helper_error_t = alloca %helper_error_t, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo, i64* %"@_key") %2 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %3 = load i64, i64* %cast, align 8 store i64 %3, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 %4 = bitcast %helper_error_t* %helper_error_t to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t, i64 0, i32 0 store i64 30006, i64* %5, align 8 %6 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t, i64 0, i32 1 store i64 0, i64* %6, align 8 %7 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t, i64 0, i32 2 store i32 0, i32* %7, align 4 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %helper_error_t*, i64)*)(i8* %0, i64 %pseudo1, i64 4294967295, %helper_error_t* %helper_error_t, i64 20) %8 = bitcast %helper_error_t* %helper_error_t to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %9 = load i64, i64* %lookup_elem_val, align 8 %10 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) %12 = add i64 %9, 1 store i64 %12, i64* %"@_newval", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo2, i64* %"@_key", i64* %"@_newval", i64 0) %13 = trunc i64 %update_elem to i32 %14 = icmp sge i32 %13, 0 br i1 %14, label %helper_merge, label %helper_failure helper_failure: ; preds = %lookup_merge %15 = bitcast %helper_error_t* %helper_error_t3 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) %16 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t3, i64 0, i32 0 store i64 30006, i64* %16, align 8 %17 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t3, i64 0, i32 1 store i64 1, i64* %17, align 8 %18 = getelementptr %helper_error_t, %helper_error_t* %helper_error_t3, i64 0, i32 2 store i32 %13, i32* %18, align 4 %pseudo4 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output5 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %helper_error_t*, i64)*)(i8* %0, i64 %pseudo4, i64 4294967295, %helper_error_t* %helper_error_t3, i64 20) %19 = bitcast %helper_error_t* %helper_error_t3 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) br label %helper_merge helper_merge: ; preds = %helper_failure, %lookup_merge %20 = bitcast i64* %"@_newval" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) %21 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %21) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/string_equal_comparison.ll000066400000000000000000000150321413460502400243620ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:vfs_read"(i8* %0) section "s_kretprobe:vfs_read_1" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %comm17 = alloca [16 x i8], align 1 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 %1 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [16 x i8]* %comm to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm, i64 16) %3 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i1 false, i1* %strcmp.result, align 1 %4 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 0 %5 = load i8, i8* %4, align 1 %strcmp.cmp = icmp ne i8 %5, 115 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp pred_false: ; preds = %strcmp.false ret i64 0 pred_true: ; preds = %strcmp.false %6 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast [16 x i8]* %comm17 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %8 = bitcast [16 x i8]* %comm17 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 16, i1 false) %get_comm18 = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm17, i64 16) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, [16 x i8]*)*)(i64 %pseudo, [16 x i8]* %comm17) %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure strcmp.false: ; preds = %strcmp.done, %strcmp.loop9, %strcmp.loop5, %strcmp.loop1, %strcmp.loop, %entry %10 = load i1, i1* %strcmp.result, align 1 %11 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = zext i1 %10 to i64 %predcond = icmp eq i64 %12, 0 br i1 %predcond, label %pred_false, label %pred_true strcmp.done: ; preds = %strcmp.loop13, %strcmp.loop_null_cmp14, %strcmp.loop_null_cmp10, %strcmp.loop_null_cmp6, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 true, i1* %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %13 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 1 %14 = load i8, i8* %13, align 1 %strcmp.cmp3 = icmp ne i8 %14, 115 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %5, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 %15 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 2 %16 = load i8, i8* %15, align 1 %strcmp.cmp7 = icmp ne i8 %16, 104 br i1 %strcmp.cmp7, label %strcmp.false, label %strcmp.loop_null_cmp6 strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %14, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 strcmp.loop5: ; preds = %strcmp.loop_null_cmp6 %17 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 3 %18 = load i8, i8* %17, align 1 %strcmp.cmp11 = icmp ne i8 %18, 100 br i1 %strcmp.cmp11, label %strcmp.false, label %strcmp.loop_null_cmp10 strcmp.loop_null_cmp6: ; preds = %strcmp.loop1 %strcmp.cmp_null8 = icmp eq i8 %16, 0 br i1 %strcmp.cmp_null8, label %strcmp.done, label %strcmp.loop5 strcmp.loop9: ; preds = %strcmp.loop_null_cmp10 %19 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 4 %20 = load i8, i8* %19, align 1 %strcmp.cmp15 = icmp ne i8 %20, 0 br i1 %strcmp.cmp15, label %strcmp.false, label %strcmp.loop_null_cmp14 strcmp.loop_null_cmp10: ; preds = %strcmp.loop5 %strcmp.cmp_null12 = icmp eq i8 %18, 0 br i1 %strcmp.cmp_null12, label %strcmp.done, label %strcmp.loop9 strcmp.loop13: ; preds = %strcmp.loop_null_cmp14 br label %strcmp.done strcmp.loop_null_cmp14: ; preds = %strcmp.loop9 %strcmp.cmp_null16 = icmp eq i8 %20, 0 br i1 %strcmp.cmp_null16, label %strcmp.done, label %strcmp.loop13 lookup_success: ; preds = %pred_true %cast = bitcast i8* %lookup_elem to i64* %21 = load i64, i64* %cast, align 8 store i64 %21, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %pred_true store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %22 = load i64, i64* %lookup_elem_val, align 8 %23 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %23) %24 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %24) %25 = add i64 %22, 1 store i64 %25, i64* %"@_val", align 8 %pseudo19 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [16 x i8]*, i64*, i64)*)(i64 %pseudo19, [16 x i8]* %comm17, i64* %"@_val", i64 0) %26 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %26) %27 = bitcast [16 x i8]* %comm17 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %27) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/string_not_equal_comparison.ll000066400000000000000000000150321413460502400252420ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kretprobe:vfs_read"(i8* %0) section "s_kretprobe:vfs_read_1" { entry: %"@_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %comm17 = alloca [16 x i8], align 1 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 %1 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [16 x i8]* %comm to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm, i64 16) %3 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i1 true, i1* %strcmp.result, align 1 %4 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 0 %5 = load i8, i8* %4, align 1 %strcmp.cmp = icmp ne i8 %5, 115 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp pred_false: ; preds = %strcmp.false ret i64 0 pred_true: ; preds = %strcmp.false %6 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast [16 x i8]* %comm17 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %8 = bitcast [16 x i8]* %comm17 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 16, i1 false) %get_comm18 = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm17, i64 16) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, [16 x i8]*)*)(i64 %pseudo, [16 x i8]* %comm17) %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure strcmp.false: ; preds = %strcmp.done, %strcmp.loop9, %strcmp.loop5, %strcmp.loop1, %strcmp.loop, %entry %10 = load i1, i1* %strcmp.result, align 1 %11 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = zext i1 %10 to i64 %predcond = icmp eq i64 %12, 0 br i1 %predcond, label %pred_false, label %pred_true strcmp.done: ; preds = %strcmp.loop13, %strcmp.loop_null_cmp14, %strcmp.loop_null_cmp10, %strcmp.loop_null_cmp6, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 false, i1* %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %13 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 1 %14 = load i8, i8* %13, align 1 %strcmp.cmp3 = icmp ne i8 %14, 115 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %5, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 %15 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 2 %16 = load i8, i8* %15, align 1 %strcmp.cmp7 = icmp ne i8 %16, 104 br i1 %strcmp.cmp7, label %strcmp.false, label %strcmp.loop_null_cmp6 strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %14, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 strcmp.loop5: ; preds = %strcmp.loop_null_cmp6 %17 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 3 %18 = load i8, i8* %17, align 1 %strcmp.cmp11 = icmp ne i8 %18, 100 br i1 %strcmp.cmp11, label %strcmp.false, label %strcmp.loop_null_cmp10 strcmp.loop_null_cmp6: ; preds = %strcmp.loop1 %strcmp.cmp_null8 = icmp eq i8 %16, 0 br i1 %strcmp.cmp_null8, label %strcmp.done, label %strcmp.loop5 strcmp.loop9: ; preds = %strcmp.loop_null_cmp10 %19 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 4 %20 = load i8, i8* %19, align 1 %strcmp.cmp15 = icmp ne i8 %20, 0 br i1 %strcmp.cmp15, label %strcmp.false, label %strcmp.loop_null_cmp14 strcmp.loop_null_cmp10: ; preds = %strcmp.loop5 %strcmp.cmp_null12 = icmp eq i8 %18, 0 br i1 %strcmp.cmp_null12, label %strcmp.done, label %strcmp.loop9 strcmp.loop13: ; preds = %strcmp.loop_null_cmp14 br label %strcmp.done strcmp.loop_null_cmp14: ; preds = %strcmp.loop9 %strcmp.cmp_null16 = icmp eq i8 %20, 0 br i1 %strcmp.cmp_null16, label %strcmp.done, label %strcmp.loop13 lookup_success: ; preds = %pred_true %cast = bitcast i8* %lookup_elem to i64* %21 = load i64, i64* %cast, align 8 store i64 %21, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %pred_true store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %22 = load i64, i64* %lookup_elem_val, align 8 %23 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %23) %24 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %24) %25 = add i64 %22, 1 store i64 %25, i64* %"@_val", align 8 %pseudo19 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [16 x i8]*, i64*, i64)*)(i64 %pseudo19, [16 x i8]* %comm17, i64* %"@_val", i64 0) %26 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %26) %27 = bitcast [16 x i8]* %comm17 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %27) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/string_propagation.ll000066400000000000000000000073241413460502400233510ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_key" = alloca i64, align 8 %lookup_elem_val = alloca [64 x i8], align 1 %"@x_key1" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %str = alloca [64 x i8], align 1 %1 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store [64 x i8] c"asdf\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %str, i64 0) %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@x_key1") %6 = bitcast [64 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %7 = bitcast [64 x i8]* %lookup_elem_val to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %lookup_elem, i64 64, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry %8 = bitcast [64 x i8]* %lookup_elem_val to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 64, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %9 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@y_key", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo3, i64* %"@y_key", [64 x i8]* %lookup_elem_val, i64 0) %11 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast [64 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/strncmp.ll000066400000000000000000000325451413460502400211310ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"tracepoint:file:filename"(i8* %0) section "s_tracepoint:file:filename_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 %str = alloca [64 x i8], align 1 %strlen = alloca i64, align 8 %1 = bitcast i64* %strlen to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast i64* %strlen to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 8, i1 false) store i64 64, i64* %strlen, align 8 %3 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %4 = bitcast [64 x i8]* %str to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 64, i1 false) %5 = ptrtoint i8* %0 to i64 %6 = add i64 %5, 8 %7 = inttoptr i64 %6 to i64* %8 = load volatile i64, i64* %7, align 8 %9 = load i64, i64* %strlen, align 8 %10 = trunc i64 %9 to i32 %probe_read_kernel_str = call i64 inttoptr (i64 115 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %str, i32 %10, i64 %8) %11 = bitcast i64* %strlen to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) %13 = bitcast [16 x i8]* %comm to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %13, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm, i64 16) %14 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i1 false, i1* %strcmp.result, align 1 %15 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 0 %16 = load i8, i8* %15, align 1 %17 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 0 %18 = load i8, i8* %17, align 1 %strcmp.cmp = icmp ne i8 %16, %18 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp pred_false: ; preds = %strcmp.false ret i64 1 pred_true: ; preds = %strcmp.false %19 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) %20 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %20) store i64 0, i64* %"@_key", align 8 %21 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %21) store i64 1, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %22 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %22) %23 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %23) ret i64 1 strcmp.false: ; preds = %strcmp.done, %strcmp.loop53, %strcmp.loop49, %strcmp.loop45, %strcmp.loop41, %strcmp.loop37, %strcmp.loop33, %strcmp.loop29, %strcmp.loop25, %strcmp.loop21, %strcmp.loop17, %strcmp.loop13, %strcmp.loop9, %strcmp.loop5, %strcmp.loop1, %strcmp.loop, %entry %24 = load i1, i1* %strcmp.result, align 1 %25 = bitcast i1* %strcmp.result to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %25) %26 = zext i1 %24 to i64 %predcond = icmp eq i64 %26, 0 br i1 %predcond, label %pred_false, label %pred_true strcmp.done: ; preds = %strcmp.loop57, %strcmp.loop_null_cmp58, %strcmp.loop_null_cmp54, %strcmp.loop_null_cmp50, %strcmp.loop_null_cmp46, %strcmp.loop_null_cmp42, %strcmp.loop_null_cmp38, %strcmp.loop_null_cmp34, %strcmp.loop_null_cmp30, %strcmp.loop_null_cmp26, %strcmp.loop_null_cmp22, %strcmp.loop_null_cmp18, %strcmp.loop_null_cmp14, %strcmp.loop_null_cmp10, %strcmp.loop_null_cmp6, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 true, i1* %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %27 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 1 %28 = load i8, i8* %27, align 1 %29 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 1 %30 = load i8, i8* %29, align 1 %strcmp.cmp3 = icmp ne i8 %28, %30 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %16, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 %31 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 2 %32 = load i8, i8* %31, align 1 %33 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 2 %34 = load i8, i8* %33, align 1 %strcmp.cmp7 = icmp ne i8 %32, %34 br i1 %strcmp.cmp7, label %strcmp.false, label %strcmp.loop_null_cmp6 strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %28, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 strcmp.loop5: ; preds = %strcmp.loop_null_cmp6 %35 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 3 %36 = load i8, i8* %35, align 1 %37 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 3 %38 = load i8, i8* %37, align 1 %strcmp.cmp11 = icmp ne i8 %36, %38 br i1 %strcmp.cmp11, label %strcmp.false, label %strcmp.loop_null_cmp10 strcmp.loop_null_cmp6: ; preds = %strcmp.loop1 %strcmp.cmp_null8 = icmp eq i8 %32, 0 br i1 %strcmp.cmp_null8, label %strcmp.done, label %strcmp.loop5 strcmp.loop9: ; preds = %strcmp.loop_null_cmp10 %39 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 4 %40 = load i8, i8* %39, align 1 %41 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 4 %42 = load i8, i8* %41, align 1 %strcmp.cmp15 = icmp ne i8 %40, %42 br i1 %strcmp.cmp15, label %strcmp.false, label %strcmp.loop_null_cmp14 strcmp.loop_null_cmp10: ; preds = %strcmp.loop5 %strcmp.cmp_null12 = icmp eq i8 %36, 0 br i1 %strcmp.cmp_null12, label %strcmp.done, label %strcmp.loop9 strcmp.loop13: ; preds = %strcmp.loop_null_cmp14 %43 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 5 %44 = load i8, i8* %43, align 1 %45 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 5 %46 = load i8, i8* %45, align 1 %strcmp.cmp19 = icmp ne i8 %44, %46 br i1 %strcmp.cmp19, label %strcmp.false, label %strcmp.loop_null_cmp18 strcmp.loop_null_cmp14: ; preds = %strcmp.loop9 %strcmp.cmp_null16 = icmp eq i8 %40, 0 br i1 %strcmp.cmp_null16, label %strcmp.done, label %strcmp.loop13 strcmp.loop17: ; preds = %strcmp.loop_null_cmp18 %47 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 6 %48 = load i8, i8* %47, align 1 %49 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 6 %50 = load i8, i8* %49, align 1 %strcmp.cmp23 = icmp ne i8 %48, %50 br i1 %strcmp.cmp23, label %strcmp.false, label %strcmp.loop_null_cmp22 strcmp.loop_null_cmp18: ; preds = %strcmp.loop13 %strcmp.cmp_null20 = icmp eq i8 %44, 0 br i1 %strcmp.cmp_null20, label %strcmp.done, label %strcmp.loop17 strcmp.loop21: ; preds = %strcmp.loop_null_cmp22 %51 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 7 %52 = load i8, i8* %51, align 1 %53 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 7 %54 = load i8, i8* %53, align 1 %strcmp.cmp27 = icmp ne i8 %52, %54 br i1 %strcmp.cmp27, label %strcmp.false, label %strcmp.loop_null_cmp26 strcmp.loop_null_cmp22: ; preds = %strcmp.loop17 %strcmp.cmp_null24 = icmp eq i8 %48, 0 br i1 %strcmp.cmp_null24, label %strcmp.done, label %strcmp.loop21 strcmp.loop25: ; preds = %strcmp.loop_null_cmp26 %55 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 8 %56 = load i8, i8* %55, align 1 %57 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 8 %58 = load i8, i8* %57, align 1 %strcmp.cmp31 = icmp ne i8 %56, %58 br i1 %strcmp.cmp31, label %strcmp.false, label %strcmp.loop_null_cmp30 strcmp.loop_null_cmp26: ; preds = %strcmp.loop21 %strcmp.cmp_null28 = icmp eq i8 %52, 0 br i1 %strcmp.cmp_null28, label %strcmp.done, label %strcmp.loop25 strcmp.loop29: ; preds = %strcmp.loop_null_cmp30 %59 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 9 %60 = load i8, i8* %59, align 1 %61 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 9 %62 = load i8, i8* %61, align 1 %strcmp.cmp35 = icmp ne i8 %60, %62 br i1 %strcmp.cmp35, label %strcmp.false, label %strcmp.loop_null_cmp34 strcmp.loop_null_cmp30: ; preds = %strcmp.loop25 %strcmp.cmp_null32 = icmp eq i8 %56, 0 br i1 %strcmp.cmp_null32, label %strcmp.done, label %strcmp.loop29 strcmp.loop33: ; preds = %strcmp.loop_null_cmp34 %63 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 10 %64 = load i8, i8* %63, align 1 %65 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 10 %66 = load i8, i8* %65, align 1 %strcmp.cmp39 = icmp ne i8 %64, %66 br i1 %strcmp.cmp39, label %strcmp.false, label %strcmp.loop_null_cmp38 strcmp.loop_null_cmp34: ; preds = %strcmp.loop29 %strcmp.cmp_null36 = icmp eq i8 %60, 0 br i1 %strcmp.cmp_null36, label %strcmp.done, label %strcmp.loop33 strcmp.loop37: ; preds = %strcmp.loop_null_cmp38 %67 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 11 %68 = load i8, i8* %67, align 1 %69 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 11 %70 = load i8, i8* %69, align 1 %strcmp.cmp43 = icmp ne i8 %68, %70 br i1 %strcmp.cmp43, label %strcmp.false, label %strcmp.loop_null_cmp42 strcmp.loop_null_cmp38: ; preds = %strcmp.loop33 %strcmp.cmp_null40 = icmp eq i8 %64, 0 br i1 %strcmp.cmp_null40, label %strcmp.done, label %strcmp.loop37 strcmp.loop41: ; preds = %strcmp.loop_null_cmp42 %71 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 12 %72 = load i8, i8* %71, align 1 %73 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 12 %74 = load i8, i8* %73, align 1 %strcmp.cmp47 = icmp ne i8 %72, %74 br i1 %strcmp.cmp47, label %strcmp.false, label %strcmp.loop_null_cmp46 strcmp.loop_null_cmp42: ; preds = %strcmp.loop37 %strcmp.cmp_null44 = icmp eq i8 %68, 0 br i1 %strcmp.cmp_null44, label %strcmp.done, label %strcmp.loop41 strcmp.loop45: ; preds = %strcmp.loop_null_cmp46 %75 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 13 %76 = load i8, i8* %75, align 1 %77 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 13 %78 = load i8, i8* %77, align 1 %strcmp.cmp51 = icmp ne i8 %76, %78 br i1 %strcmp.cmp51, label %strcmp.false, label %strcmp.loop_null_cmp50 strcmp.loop_null_cmp46: ; preds = %strcmp.loop41 %strcmp.cmp_null48 = icmp eq i8 %72, 0 br i1 %strcmp.cmp_null48, label %strcmp.done, label %strcmp.loop45 strcmp.loop49: ; preds = %strcmp.loop_null_cmp50 %79 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 14 %80 = load i8, i8* %79, align 1 %81 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 14 %82 = load i8, i8* %81, align 1 %strcmp.cmp55 = icmp ne i8 %80, %82 br i1 %strcmp.cmp55, label %strcmp.false, label %strcmp.loop_null_cmp54 strcmp.loop_null_cmp50: ; preds = %strcmp.loop45 %strcmp.cmp_null52 = icmp eq i8 %76, 0 br i1 %strcmp.cmp_null52, label %strcmp.done, label %strcmp.loop49 strcmp.loop53: ; preds = %strcmp.loop_null_cmp54 %83 = getelementptr [64 x i8], [64 x i8]* %str, i32 0, i32 15 %84 = load i8, i8* %83, align 1 %85 = getelementptr [16 x i8], [16 x i8]* %comm, i32 0, i32 15 %86 = load i8, i8* %85, align 1 %strcmp.cmp59 = icmp ne i8 %84, %86 br i1 %strcmp.cmp59, label %strcmp.false, label %strcmp.loop_null_cmp58 strcmp.loop_null_cmp54: ; preds = %strcmp.loop49 %strcmp.cmp_null56 = icmp eq i8 %80, 0 br i1 %strcmp.cmp_null56, label %strcmp.done, label %strcmp.loop53 strcmp.loop57: ; preds = %strcmp.loop_null_cmp58 br label %strcmp.done strcmp.loop_null_cmp58: ; preds = %strcmp.loop53 %strcmp.cmp_null60 = icmp eq i8 %84, 0 br i1 %strcmp.cmp_null60, label %strcmp.done, label %strcmp.loop57 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/struct_char_1.ll000066400000000000000000000040531413460502400221750ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i8, align 1 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct Foo.x", i32 1, i64 %5) %6 = load i8, i8* %"struct Foo.x", align 1 %7 = sext i8 %6 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct Foo.x") %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 0, i64* %"@x_key", align 8 %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 %7, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_char_2.ll000066400000000000000000000040531413460502400221760ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i8, align 1 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct Foo.x", i32 1, i64 %5) %6 = load i8, i8* %"struct Foo.x", align 1 %7 = sext i8 %6 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct Foo.x") %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 0, i64* %"@x_key", align 8 %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 %7, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_integer_ptr_1.ll000066400000000000000000000047231413460502400236060ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %deref = alloca i32, align 4 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.x", i32 8, i64 %5) %7 = load i64, i64* %"struct Foo.x", align 8 %8 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i32* %deref to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %deref, i32 4, i64 %7) %10 = load i32, i32* %deref, align 4 %11 = sext i32 %10 to i64 %12 = bitcast i32* %deref to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) store i64 0, i64* %"@x_key", align 8 %14 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 %11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_integer_ptr_2.ll000066400000000000000000000047231413460502400236070ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %deref = alloca i32, align 4 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.x", i32 8, i64 %5) %7 = load i64, i64* %"struct Foo.x", align 8 %8 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i32* %deref to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %deref, i32 4, i64 %7) %10 = load i32, i32* %deref, align 4 %11 = sext i32 %10 to i64 %12 = bitcast i32* %deref to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) store i64 0, i64* %"@x_key", align 8 %14 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 %11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_integers_1.ll000066400000000000000000000041611413460502400231000ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i32* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.x", i32 4, i64 %5) %7 = load i32, i32* %"struct Foo.x", align 4 %8 = sext i32 %7 to i64 %9 = bitcast i32* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_integers_2.ll000066400000000000000000000041611413460502400231010ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i32* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.x", i32 4, i64 %5) %7 = load i32, i32* %"struct Foo.x", align 4 %8 = sext i32 %7 to i64 %9 = bitcast i32* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_long_1.ll000066400000000000000000000041251413460502400222170ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.x", i32 8, i64 %5) %7 = load i64, i64* %"struct Foo.x", align 8 %8 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 %7, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_long_2.ll000066400000000000000000000041251413460502400222200ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.x", i32 8, i64 %5) %7 = load i64, i64* %"struct Foo.x", align 8 %8 = bitcast i64* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 %7, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_nested_struct_anon_1.ll000066400000000000000000000044671413460502400251720ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo::(anonymous at definitions.h:2:14).x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = add i64 %5, 0 %7 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo::(anonymous at definitions.h:2:14).x", i32 4, i64 %6) %8 = load i32, i32* %"struct Foo::(anonymous at definitions.h:2:14).x", align 4 %9 = sext i32 %8 to i64 %10 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@x_key", align 8 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) store i64 %9, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_nested_struct_anon_2.ll000066400000000000000000000044671413460502400251730ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo::(anonymous at definitions.h:2:14).x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = add i64 %5, 0 %7 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo::(anonymous at definitions.h:2:14).x", i32 4, i64 %6) %8 = load i32, i32* %"struct Foo::(anonymous at definitions.h:2:14).x", align 4 %9 = sext i32 %8 to i64 %10 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@x_key", align 8 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) store i64 %9, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_nested_struct_named_1.ll000066400000000000000000000042101413460502400253050ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = add i64 %5, 0 %7 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %6) %8 = load i32, i32* %"struct Bar.x", align 4 %9 = sext i32 %8 to i64 %10 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@x_key", align 8 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) store i64 %9, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_nested_struct_named_2.ll000066400000000000000000000042101413460502400253060ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = add i64 %5, 0 %7 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %6) %8 = load i32, i32* %"struct Bar.x", align 4 %9 = sext i32 %8 to i64 %10 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@x_key", align 8 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) store i64 %9, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_nested_struct_ptr_named_1.ll000066400000000000000000000050411413460502400261750ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"struct Foo.bar" = alloca i64, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i64* %"struct Foo.bar" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.bar", i32 8, i64 %5) %7 = load i64, i64* %"struct Foo.bar", align 8 %8 = bitcast i64* %"struct Foo.bar" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = add i64 %7, 0 %10 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %9) %11 = load i32, i32* %"struct Bar.x", align 4 %12 = sext i32 %11 to i64 %13 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 0, i64* %"@x_key", align 8 %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) store i64 %12, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %16 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) %17 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %17) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_nested_struct_ptr_named_2.ll000066400000000000000000000050411413460502400261760ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"struct Foo.bar" = alloca i64, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i64* %"struct Foo.bar" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.bar", i32 8, i64 %5) %7 = load i64, i64* %"struct Foo.bar", align 8 %8 = bitcast i64* %"struct Foo.bar" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = add i64 %7, 0 %10 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %9) %11 = load i32, i32* %"struct Bar.x", align 4 %12 = sext i32 %11 to i64 %13 = bitcast i32* %"struct Bar.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 0, i64* %"@x_key", align 8 %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) store i64 %12, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %16 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) %17 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %17) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_save_1.ll000066400000000000000000000031111413460502400222100ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@foo_val" = alloca [12 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@foo_key", align 8 %4 = bitcast [12 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([12 x i8]*, i32, i64)*)([12 x i8]* %"@foo_val", i32 12, i64 %arg0) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [12 x i8]*, i64)*)(i64 %pseudo, i64* %"@foo_key", [12 x i8]* %"@foo_val", i64 0) %5 = bitcast [12 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_save_2.ll000066400000000000000000000031111413460502400222110ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@foo_val" = alloca [12 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@foo_key", align 8 %4 = bitcast [12 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([12 x i8]*, i32, i64)*)([12 x i8]* %"@foo_val", i32 12, i64 %arg0) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [12 x i8]*, i64)*)(i64 %pseudo, i64* %"@foo_key", [12 x i8]* %"@foo_val", i64 0) %5 = bitcast [12 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_save_nested.ll000066400000000000000000000143031413460502400233370ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %lookup_elem_val11 = alloca [16 x i8], align 1 %"@foo_key5" = alloca i64, align 8 %"@bar_key" = alloca i64, align 8 %lookup_elem_val = alloca [16 x i8], align 1 %"@foo_key1" = alloca i64, align 8 %"@foo_val" = alloca [16 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@foo_key", align 8 %4 = bitcast [16 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([16 x i8]*, i32, i64)*)([16 x i8]* %"@foo_val", i32 16, i64 %arg0) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [16 x i8]*, i64)*)(i64 %pseudo, i64* %"@foo_key", [16 x i8]* %"@foo_val", i64 0) %5 = bitcast [16 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@foo_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@foo_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@foo_key1") %8 = bitcast [16 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %9 = bitcast [16 x i8]* %lookup_elem_val to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %9, i8* align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry %10 = bitcast [16 x i8]* %lookup_elem_val to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %10, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %11 = bitcast i64* %"@foo_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = getelementptr [16 x i8], [16 x i8]* %lookup_elem_val, i32 0, i64 4 %13 = bitcast i8* %12 to [8 x i8]* %14 = bitcast i64* %"@bar_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 0, i64* %"@bar_key", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [8 x i8]*, i64)*)(i64 %pseudo3, i64* %"@bar_key", [8 x i8]* %13, i64 0) %15 = bitcast i64* %"@bar_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast [16 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) %17 = bitcast i64* %"@foo_key5" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %17) store i64 0, i64* %"@foo_key5", align 8 %pseudo6 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %lookup_elem7 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo6, i64* %"@foo_key5") %18 = bitcast [16 x i8]* %lookup_elem_val11 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %18) %map_lookup_cond12 = icmp ne i8* %lookup_elem7, null br i1 %map_lookup_cond12, label %lookup_success8, label %lookup_failure9 lookup_success8: ; preds = %lookup_merge %19 = bitcast [16 x i8]* %lookup_elem_val11 to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %19, i8* align 1 %lookup_elem7, i64 16, i1 false) br label %lookup_merge10 lookup_failure9: ; preds = %lookup_merge %20 = bitcast [16 x i8]* %lookup_elem_val11 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %20, i8 0, i64 16, i1 false) br label %lookup_merge10 lookup_merge10: ; preds = %lookup_failure9, %lookup_success8 %21 = bitcast i64* %"@foo_key5" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %21) %22 = getelementptr [16 x i8], [16 x i8]* %lookup_elem_val11, i32 0, i64 4 %23 = bitcast i8* %22 to [8 x i8]* %24 = getelementptr [8 x i8], [8 x i8]* %23, i32 0, i64 0 %25 = bitcast i8* %24 to i32* %26 = load volatile i32, i32* %25, align 4 %27 = bitcast [16 x i8]* %lookup_elem_val11 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %27) %28 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %28) store i64 0, i64* %"@x_key", align 8 %29 = sext i32 %26 to i64 %30 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %30) store i64 %29, i64* %"@x_val", align 8 %pseudo13 = call i64 @llvm.bpf.pseudo(i64 1, i64 2) %update_elem14 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo13, i64* %"@x_key", i64* %"@x_val", i64 0) %31 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %31) %32 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %32) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/struct_save_string.ll000066400000000000000000000076011413460502400233660ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@str_key" = alloca i64, align 8 %lookup_elem_val = alloca [32 x i8], align 1 %"@foo_key1" = alloca i64, align 8 %"@foo_val" = alloca [32 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = bitcast i8* %0 to i64* %2 = getelementptr i64, i64* %1, i64 14 %arg0 = load volatile i64, i64* %2, align 8 %3 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) store i64 0, i64* %"@foo_key", align 8 %4 = bitcast [32 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([32 x i8]*, i32, i64)*)([32 x i8]* %"@foo_val", i32 32, i64 %arg0) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [32 x i8]*, i64)*)(i64 %pseudo, i64* %"@foo_key", [32 x i8]* %"@foo_val", i64 0) %5 = bitcast [32 x i8]* %"@foo_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) %6 = bitcast i64* %"@foo_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %6) %7 = bitcast i64* %"@foo_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@foo_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@foo_key1") %8 = bitcast [32 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %9 = bitcast [32 x i8]* %lookup_elem_val to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %9, i8* align 1 %lookup_elem, i64 32, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry %10 = bitcast [32 x i8]* %lookup_elem_val to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %10, i8 0, i64 32, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %11 = bitcast i64* %"@foo_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = getelementptr [32 x i8], [32 x i8]* %lookup_elem_val, i32 0, i64 0 %13 = bitcast i8* %12 to [32 x i8]* %14 = bitcast i64* %"@str_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 0, i64* %"@str_key", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [32 x i8]*, i64)*)(i64 %pseudo3, i64* %"@str_key", [32 x i8]* %13, i64 0) %15 = bitcast i64* %"@str_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast [32 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/struct_semicolon_1.ll000066400000000000000000000037211413460502400232510ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %printf_args = alloca %printf_t, align 8 %1 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %3 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %3, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %get_stackid = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo, i64 256) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %4 = shl i64 %get_pid_tgid, 32 %5 = or i64 %get_stackid, %4 %6 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %5, i64* %6, align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo1, i64 4294967295, %printf_t* %printf_args, i64 16) %7 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/struct_semicolon_2.ll000066400000000000000000000037211413460502400232520ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %printf_args = alloca %printf_t, align 8 %1 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %3 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %3, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %get_stackid = call i64 inttoptr (i64 27 to i64 (i8*, i64, i64)*)(i8* %0, i64 %pseudo, i64 256) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %4 = shl i64 %get_pid_tgid, 32 %5 = or i64 %get_stackid, %4 %6 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %5, i64* %6, align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo1, i64 4294967295, %printf_t* %printf_args, i64 16) %7 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/struct_short_1.ll000066400000000000000000000041611413460502400224170ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i16, align 2 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i16* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i16*, i32, i64)*)(i16* %"struct Foo.x", i32 2, i64 %5) %7 = load i16, i16* %"struct Foo.x", align 2 %8 = sext i16 %7 to i64 %9 = bitcast i16* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_short_2.ll000066400000000000000000000041611413460502400224200ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i16, align 2 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast i16* %"struct Foo.x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i16*, i32, i64)*)(i16* %"struct Foo.x", i32 2, i64 %5) %7 = load i16, i16* %"struct Foo.x", align 2 %8 = sext i16 %7 to i64 %9 = bitcast i16* %"struct Foo.x" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_string_array_1.ll000066400000000000000000000035611413460502400237670ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@mystr_key" = alloca i64, align 8 %"struct Foo.str" = alloca [32 x i8], align 1 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast [32 x i8]* %"struct Foo.str" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([32 x i8]*, i32, i64)*)([32 x i8]* %"struct Foo.str", i32 32, i64 %5) %7 = bitcast i64* %"@mystr_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@mystr_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [32 x i8]*, i64)*)(i64 %pseudo, i64* %"@mystr_key", [32 x i8]* %"struct Foo.str", i64 0) %8 = bitcast i64* %"@mystr_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast [32 x i8]* %"struct Foo.str" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_string_array_2.ll000066400000000000000000000035611413460502400237700ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@mystr_key" = alloca i64, align 8 %"struct Foo.str" = alloca [32 x i8], align 1 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = load i64, i64* %"$foo", align 8 %5 = add i64 %4, 0 %6 = bitcast [32 x i8]* %"struct Foo.str" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([32 x i8]*, i32, i64)*)([32 x i8]* %"struct Foo.str", i32 32, i64 %5) %7 = bitcast i64* %"@mystr_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@mystr_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [32 x i8]*, i64)*)(i64 %pseudo, i64* %"@mystr_key", [32 x i8]* %"struct Foo.str", i64 0) %8 = bitcast i64* %"@mystr_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast [32 x i8]* %"struct Foo.str" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/struct_string_ptr.ll000066400000000000000000000057401413460502400232370ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@mystr_key" = alloca i64, align 8 %"struct Foo.str" = alloca i64, align 8 %str = alloca [64 x i8], align 1 %strlen = alloca i64, align 8 %"$foo" = alloca i64, align 8 %1 = bitcast i64* %"$foo" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$foo", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 store i64 %arg0, i64* %"$foo", align 8 %4 = bitcast i64* %strlen to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %strlen to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 8, i1 false) store i64 64, i64* %strlen, align 8 %6 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %7 = bitcast [64 x i8]* %str to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %7, i8 0, i64 64, i1 false) %8 = load i64, i64* %"$foo", align 8 %9 = add i64 %8, 0 %10 = bitcast i64* %"struct Foo.str" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i64*, i32, i64)*)(i64* %"struct Foo.str", i32 8, i64 %9) %11 = load i64, i64* %"struct Foo.str", align 8 %12 = bitcast i64* %"struct Foo.str" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = load i64, i64* %strlen, align 8 %14 = trunc i64 %13 to i32 %probe_read_kernel_str = call i64 inttoptr (i64 115 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %str, i32 %14, i64 %11) %15 = bitcast i64* %strlen to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@mystr_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) store i64 0, i64* %"@mystr_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@mystr_key", [64 x i8]* %str, i64 0) %17 = bitcast i64* %"@mystr_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %17) %18 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %18) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/ternary_int.ll000066400000000000000000000042041413460502400217700ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %buf = alloca i64, align 8 %result = alloca i64, align 8 %1 = bitcast i64* %result to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast i64* %buf to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %3 = lshr i64 %get_pid_tgid, 32 %4 = icmp ult i64 %3, 10000 %5 = zext i1 %4 to i64 %true_cond = icmp ne i64 %5, 0 br i1 %true_cond, label %left, label %right left: ; preds = %entry store i64 1, i64* %result, align 8 br label %done right: ; preds = %entry store i64 2, i64* %result, align 8 br label %done done: ; preds = %right, %left %6 = load i64, i64* %result, align 8 %7 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@x_key", align 8 %8 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %6, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/ternary_none.ll000066400000000000000000000051061413460502400221370ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t = type { i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %perfdata = alloca i64, align 8 %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %1 = lshr i64 %get_pid_tgid, 32 %2 = icmp ult i64 %1, 10000 %3 = zext i1 %2 to i64 %true_cond = icmp ne i64 %3, 0 br i1 %true_cond, label %left, label %right left: ; preds = %entry %4 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %5, i8 0, i64 8, i1 false) %6 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %6, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 8) %7 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) br label %done right: ; preds = %entry %8 = bitcast i64* %perfdata to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 30000, i64* %perfdata, align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output2 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, i64*, i64)*)(i8* %0, i64 %pseudo1, i64 4294967295, i64* %perfdata, i64 8) %9 = bitcast i64* %perfdata to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) ret i64 0 done: ; preds = %deadcode, %left ret i64 0 deadcode: ; No predecessors! br label %done } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/ternary_str.ll000066400000000000000000000064731413460502400220200ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 %str1 = alloca [64 x i8], align 1 %str = alloca [64 x i8], align 1 %buf = alloca [64 x i8], align 1 %result = alloca [64 x i8], align 1 %1 = bitcast [64 x i8]* %result to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [64 x i8]* %buf to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %get_pid_tgid = call i64 inttoptr (i64 14 to i64 ()*)() %3 = lshr i64 %get_pid_tgid, 32 %4 = icmp ult i64 %3, 10000 %5 = zext i1 %4 to i64 %true_cond = icmp ne i64 %5, 0 br i1 %true_cond, label %left, label %right left: ; preds = %entry %6 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) store [64 x i8] c"lo\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 %7 = bitcast [64 x i8]* %buf to i8* %8 = bitcast [64 x i8]* %str to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 64, i1 false) br label %done right: ; preds = %entry %9 = bitcast [64 x i8]* %str1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store [64 x i8] c"hi\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str1, align 1 %10 = bitcast [64 x i8]* %buf to i8* %11 = bitcast [64 x i8]* %str1 to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %10, i8* align 1 %11, i64 64, i1 false) br label %done done: ; preds = %right, %left %12 = bitcast [64 x i8]* %str1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %buf, i64 0) %15 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast [64 x i8]* %buf to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/tuple.ll000066400000000000000000000061011413460502400205610ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %"int64_int64_string[64]__tuple_t" = type { i64, i64, [64 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@t_key" = alloca i64, align 8 %str = alloca [64 x i8], align 1 %tuple = alloca %"int64_int64_string[64]__tuple_t", align 8 %1 = bitcast %"int64_int64_string[64]__tuple_t"* %tuple to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %"int64_int64_string[64]__tuple_t"* %tuple to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 80, i1 false) %3 = getelementptr %"int64_int64_string[64]__tuple_t", %"int64_int64_string[64]__tuple_t"* %tuple, i32 0, i32 0 store i64 1, i64* %3, align 8 %4 = getelementptr %"int64_int64_string[64]__tuple_t", %"int64_int64_string[64]__tuple_t"* %tuple, i32 0, i32 1 store i64 2, i64* %4, align 8 %5 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store [64 x i8] c"str\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 %6 = getelementptr %"int64_int64_string[64]__tuple_t", %"int64_int64_string[64]__tuple_t"* %tuple, i32 0, i32 2 %7 = bitcast [64 x i8]* %6 to i8* %8 = bitcast [64 x i8]* %str to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 64, i1 false) %9 = bitcast [64 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@t_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@t_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %"int64_int64_string[64]__tuple_t"*, i64)*)(i64 %pseudo, i64* %"@t_key", %"int64_int64_string[64]__tuple_t"* %tuple, i64 0) %11 = bitcast i64* %"@t_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast %"int64_int64_string[64]__tuple_t"* %tuple to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/tuple_array_struct.ll000066400000000000000000000050471413460502400233730ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %"struct Foo_int32[4]__tuple_t" = type { [8 x i8], [4 x i32] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@t_key" = alloca i64, align 8 %tuple = alloca %"struct Foo_int32[4]__tuple_t", align 8 %1 = bitcast %"struct Foo_int32[4]__tuple_t"* %tuple to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast %"struct Foo_int32[4]__tuple_t"* %tuple to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 24, i1 false) %3 = bitcast i8* %0 to i64* %4 = getelementptr i64, i64* %3, i64 14 %arg0 = load volatile i64, i64* %4, align 8 %5 = getelementptr %"struct Foo_int32[4]__tuple_t", %"struct Foo_int32[4]__tuple_t"* %tuple, i32 0, i32 0 %probe_read_kernel = call i64 inttoptr (i64 113 to i64 ([8 x i8]*, i32, i64)*)([8 x i8]* %5, i32 8, i64 %arg0) %6 = bitcast i8* %0 to i64* %7 = getelementptr i64, i64* %6, i64 13 %arg1 = load volatile i64, i64* %7, align 8 %8 = add i64 %arg1, 0 %9 = getelementptr %"struct Foo_int32[4]__tuple_t", %"struct Foo_int32[4]__tuple_t"* %tuple, i32 0, i32 1 %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 ([4 x i32]*, i32, i64)*)([4 x i32]* %9, i32 16, i64 %8) %10 = bitcast i64* %"@t_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@t_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %"struct Foo_int32[4]__tuple_t"*, i64)*)(i64 %pseudo, i64* %"@t_key", %"struct Foo_int32[4]__tuple_t"* %tuple, i64 0) %11 = bitcast i64* %"@t_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) %12 = bitcast %"struct Foo_int32[4]__tuple_t"* %tuple to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/unroll.ll000066400000000000000000000266011413460502400207520ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %"@i_val56" = alloca i64, align 8 %"@i_key55" = alloca i64, align 8 %lookup_elem_val52 = alloca i64, align 8 %"@i_key46" = alloca i64, align 8 %"@i_val43" = alloca i64, align 8 %"@i_key42" = alloca i64, align 8 %lookup_elem_val39 = alloca i64, align 8 %"@i_key33" = alloca i64, align 8 %"@i_val30" = alloca i64, align 8 %"@i_key29" = alloca i64, align 8 %lookup_elem_val26 = alloca i64, align 8 %"@i_key20" = alloca i64, align 8 %"@i_val17" = alloca i64, align 8 %"@i_key16" = alloca i64, align 8 %lookup_elem_val13 = alloca i64, align 8 %"@i_key7" = alloca i64, align 8 %"@i_val4" = alloca i64, align 8 %"@i_key3" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@i_key1" = alloca i64, align 8 %"@i_val" = alloca i64, align 8 %"@i_key" = alloca i64, align 8 %1 = bitcast i64* %"@i_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@i_key", align 8 %2 = bitcast i64* %"@i_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@i_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@i_key", i64* %"@i_val", i64 0) %3 = bitcast i64* %"@i_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@i_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@i_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@i_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@i_key1") %6 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %cast = bitcast i8* %lookup_elem to i64* %7 = load i64, i64* %cast, align 8 store i64 %7, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, i64* %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, i64* %lookup_elem_val, align 8 %9 = bitcast i64* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@i_key1" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = add i64 %8, 1 %12 = bitcast i64* %"@i_key3" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) store i64 0, i64* %"@i_key3", align 8 %13 = bitcast i64* %"@i_val4" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) store i64 %11, i64* %"@i_val4", align 8 %pseudo5 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem6 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo5, i64* %"@i_key3", i64* %"@i_val4", i64 0) %14 = bitcast i64* %"@i_val4" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) %15 = bitcast i64* %"@i_key3" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast i64* %"@i_key7" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) store i64 0, i64* %"@i_key7", align 8 %pseudo8 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem9 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo8, i64* %"@i_key7") %17 = bitcast i64* %lookup_elem_val13 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %17) %map_lookup_cond14 = icmp ne i8* %lookup_elem9, null br i1 %map_lookup_cond14, label %lookup_success10, label %lookup_failure11 lookup_success10: ; preds = %lookup_merge %cast15 = bitcast i8* %lookup_elem9 to i64* %18 = load i64, i64* %cast15, align 8 store i64 %18, i64* %lookup_elem_val13, align 8 br label %lookup_merge12 lookup_failure11: ; preds = %lookup_merge store i64 0, i64* %lookup_elem_val13, align 8 br label %lookup_merge12 lookup_merge12: ; preds = %lookup_failure11, %lookup_success10 %19 = load i64, i64* %lookup_elem_val13, align 8 %20 = bitcast i64* %lookup_elem_val13 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) %21 = bitcast i64* %"@i_key7" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %21) %22 = add i64 %19, 1 %23 = bitcast i64* %"@i_key16" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %23) store i64 0, i64* %"@i_key16", align 8 %24 = bitcast i64* %"@i_val17" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %24) store i64 %22, i64* %"@i_val17", align 8 %pseudo18 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem19 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo18, i64* %"@i_key16", i64* %"@i_val17", i64 0) %25 = bitcast i64* %"@i_val17" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %25) %26 = bitcast i64* %"@i_key16" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %26) %27 = bitcast i64* %"@i_key20" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %27) store i64 0, i64* %"@i_key20", align 8 %pseudo21 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem22 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo21, i64* %"@i_key20") %28 = bitcast i64* %lookup_elem_val26 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %28) %map_lookup_cond27 = icmp ne i8* %lookup_elem22, null br i1 %map_lookup_cond27, label %lookup_success23, label %lookup_failure24 lookup_success23: ; preds = %lookup_merge12 %cast28 = bitcast i8* %lookup_elem22 to i64* %29 = load i64, i64* %cast28, align 8 store i64 %29, i64* %lookup_elem_val26, align 8 br label %lookup_merge25 lookup_failure24: ; preds = %lookup_merge12 store i64 0, i64* %lookup_elem_val26, align 8 br label %lookup_merge25 lookup_merge25: ; preds = %lookup_failure24, %lookup_success23 %30 = load i64, i64* %lookup_elem_val26, align 8 %31 = bitcast i64* %lookup_elem_val26 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %31) %32 = bitcast i64* %"@i_key20" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %32) %33 = add i64 %30, 1 %34 = bitcast i64* %"@i_key29" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %34) store i64 0, i64* %"@i_key29", align 8 %35 = bitcast i64* %"@i_val30" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %35) store i64 %33, i64* %"@i_val30", align 8 %pseudo31 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem32 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo31, i64* %"@i_key29", i64* %"@i_val30", i64 0) %36 = bitcast i64* %"@i_val30" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %36) %37 = bitcast i64* %"@i_key29" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %37) %38 = bitcast i64* %"@i_key33" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %38) store i64 0, i64* %"@i_key33", align 8 %pseudo34 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem35 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo34, i64* %"@i_key33") %39 = bitcast i64* %lookup_elem_val39 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %39) %map_lookup_cond40 = icmp ne i8* %lookup_elem35, null br i1 %map_lookup_cond40, label %lookup_success36, label %lookup_failure37 lookup_success36: ; preds = %lookup_merge25 %cast41 = bitcast i8* %lookup_elem35 to i64* %40 = load i64, i64* %cast41, align 8 store i64 %40, i64* %lookup_elem_val39, align 8 br label %lookup_merge38 lookup_failure37: ; preds = %lookup_merge25 store i64 0, i64* %lookup_elem_val39, align 8 br label %lookup_merge38 lookup_merge38: ; preds = %lookup_failure37, %lookup_success36 %41 = load i64, i64* %lookup_elem_val39, align 8 %42 = bitcast i64* %lookup_elem_val39 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %42) %43 = bitcast i64* %"@i_key33" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %43) %44 = add i64 %41, 1 %45 = bitcast i64* %"@i_key42" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %45) store i64 0, i64* %"@i_key42", align 8 %46 = bitcast i64* %"@i_val43" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %46) store i64 %44, i64* %"@i_val43", align 8 %pseudo44 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem45 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo44, i64* %"@i_key42", i64* %"@i_val43", i64 0) %47 = bitcast i64* %"@i_val43" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %47) %48 = bitcast i64* %"@i_key42" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %48) %49 = bitcast i64* %"@i_key46" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %49) store i64 0, i64* %"@i_key46", align 8 %pseudo47 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem48 = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo47, i64* %"@i_key46") %50 = bitcast i64* %lookup_elem_val52 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %50) %map_lookup_cond53 = icmp ne i8* %lookup_elem48, null br i1 %map_lookup_cond53, label %lookup_success49, label %lookup_failure50 lookup_success49: ; preds = %lookup_merge38 %cast54 = bitcast i8* %lookup_elem48 to i64* %51 = load i64, i64* %cast54, align 8 store i64 %51, i64* %lookup_elem_val52, align 8 br label %lookup_merge51 lookup_failure50: ; preds = %lookup_merge38 store i64 0, i64* %lookup_elem_val52, align 8 br label %lookup_merge51 lookup_merge51: ; preds = %lookup_failure50, %lookup_success49 %52 = load i64, i64* %lookup_elem_val52, align 8 %53 = bitcast i64* %lookup_elem_val52 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %53) %54 = bitcast i64* %"@i_key46" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %54) %55 = add i64 %52, 1 %56 = bitcast i64* %"@i_key55" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %56) store i64 0, i64* %"@i_key55", align 8 %57 = bitcast i64* %"@i_val56" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %57) store i64 %55, i64* %"@i_val56", align 8 %pseudo57 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem58 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo57, i64* %"@i_key55", i64* %"@i_val56", i64 0) %58 = bitcast i64* %"@i_val56" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %58) %59 = bitcast i64* %"@i_key55" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %59) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/unroll_async_id.ll000066400000000000000000000121671413460502400226250ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t.3 = type { i64 } %printf_t.2 = type { i64 } %printf_t.1 = type { i64 } %printf_t.0 = type { i64 } %printf_t = type { i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %printf_args11 = alloca %printf_t.3, align 8 %printf_args8 = alloca %printf_t.2, align 8 %printf_args5 = alloca %printf_t.1, align 8 %printf_args2 = alloca %printf_t.0, align 8 %printf_args = alloca %printf_t, align 8 %"@i_val" = alloca i64, align 8 %"@i_key" = alloca i64, align 8 %1 = bitcast i64* %"@i_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"@i_key", align 8 %2 = bitcast i64* %"@i_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@i_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@i_key", i64* %"@i_val", i64 0) %3 = bitcast i64* %"@i_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) %4 = bitcast i64* %"@i_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) %6 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %6, i8 0, i64 8, i1 false) %7 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %7, align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo1, i64 4294967295, %printf_t* %printf_args, i64 8) %8 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast %printf_t.0* %printf_args2 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %10 = bitcast %printf_t.0* %printf_args2 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %10, i8 0, i64 8, i1 false) %11 = getelementptr %printf_t.0, %printf_t.0* %printf_args2, i32 0, i32 0 store i64 0, i64* %11, align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output4 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.0*, i64)*)(i8* %0, i64 %pseudo3, i64 4294967295, %printf_t.0* %printf_args2, i64 8) %12 = bitcast %printf_t.0* %printf_args2 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) %13 = bitcast %printf_t.1* %printf_args5 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) %14 = bitcast %printf_t.1* %printf_args5 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %14, i8 0, i64 8, i1 false) %15 = getelementptr %printf_t.1, %printf_t.1* %printf_args5, i32 0, i32 0 store i64 0, i64* %15, align 8 %pseudo6 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output7 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.1*, i64)*)(i8* %0, i64 %pseudo6, i64 4294967295, %printf_t.1* %printf_args5, i64 8) %16 = bitcast %printf_t.1* %printf_args5 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) %17 = bitcast %printf_t.2* %printf_args8 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %17) %18 = bitcast %printf_t.2* %printf_args8 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %18, i8 0, i64 8, i1 false) %19 = getelementptr %printf_t.2, %printf_t.2* %printf_args8, i32 0, i32 0 store i64 0, i64* %19, align 8 %pseudo9 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output10 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.2*, i64)*)(i8* %0, i64 %pseudo9, i64 4294967295, %printf_t.2* %printf_args8, i64 8) %20 = bitcast %printf_t.2* %printf_args8 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) %21 = bitcast %printf_t.3* %printf_args11 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %21) %22 = bitcast %printf_t.3* %printf_args11 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %22, i8 0, i64 8, i1 false) %23 = getelementptr %printf_t.3, %printf_t.3* %printf_args11, i32 0, i32 0 store i64 0, i64* %23, align 8 %pseudo12 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %perf_event_output13 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.3*, i64)*)(i8* %0, i64 %pseudo12, i64 4294967295, %printf_t.3* %printf_args11, i64 8) %24 = bitcast %printf_t.3* %printf_args11 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %24) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/variable.ll000066400000000000000000000054411413460502400212230ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_key" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"$var" = alloca [16 x i8], align 1 %1 = bitcast [16 x i8]* %"$var" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) %2 = bitcast [16 x i8]* %"$var" to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) %comm = alloca [16 x i8], align 1 %3 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %3) %4 = bitcast [16 x i8]* %comm to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %4, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 ([16 x i8]*, i64)*)([16 x i8]* %comm, i64 16) %5 = bitcast [16 x i8]* %"$var" to i8* %6 = bitcast [16 x i8]* %comm to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %5, i8* align 1 %6, i64 16, i1 false) %7 = bitcast [16 x i8]* %comm to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7) %8 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [16 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [16 x i8]* %"$var", i64 0) %9 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@y_key", align 8 %pseudo1 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %update_elem2 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [16 x i8]*, i64)*)(i64 %pseudo1, i64* %"@y_key", [16 x i8]* %"$var", i64 0) %11 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/variable_assign_array.ll000066400000000000000000000041761413460502400237710ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %array_access = alloca i32, align 4 %"$var" = alloca i64, align 8 %1 = bitcast i64* %"$var" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$var", align 8 %2 = bitcast i8* %0 to i64* %3 = getelementptr i64, i64* %2, i64 14 %arg0 = load volatile i64, i64* %3, align 8 %4 = add i64 %arg0, 0 store i64 %4, i64* %"$var", align 8 %5 = load i64, i64* %"$var", align 8 %6 = add i64 %5, 0 %7 = bitcast i32* %array_access to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %array_access, i32 4, i64 %6) %8 = load i32, i32* %array_access, align 4 %9 = sext i32 %8 to i64 %10 = bitcast i32* %array_access to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) %11 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) store i64 0, i64* %"@x_key", align 8 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) store i64 %9, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %14) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } bpftrace-0.14.0/tests/codegen/llvm/variable_increment_decrement.ll000066400000000000000000000113611413460502400253130ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" %printf_t.2 = type { i64, i64 } %printf_t.1 = type { i64, i64 } %printf_t.0 = type { i64, i64 } %printf_t = type { i64, i64 } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN(i8* %0) section "s_BEGIN_1" { entry: %printf_args7 = alloca %printf_t.2, align 8 %printf_args4 = alloca %printf_t.1, align 8 %printf_args1 = alloca %printf_t.0, align 8 %printf_args = alloca %printf_t, align 8 %"$x" = alloca i64, align 8 %1 = bitcast i64* %"$x" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$x", align 8 store i64 10, i64* %"$x", align 8 %2 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) %3 = bitcast %printf_t* %printf_args to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %3, i8 0, i64 16, i1 false) %4 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 0 store i64 0, i64* %4, align 8 %5 = load i64, i64* %"$x", align 8 %6 = add i64 %5, 1 store i64 %6, i64* %"$x", align 8 %7 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 store i64 %5, i64* %7, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 16) %8 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) %9 = bitcast %printf_t.0* %printf_args1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) %10 = bitcast %printf_t.0* %printf_args1 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %10, i8 0, i64 16, i1 false) %11 = getelementptr %printf_t.0, %printf_t.0* %printf_args1, i32 0, i32 0 store i64 1, i64* %11, align 8 %12 = load i64, i64* %"$x", align 8 %13 = add i64 %12, 1 store i64 %13, i64* %"$x", align 8 %14 = getelementptr %printf_t.0, %printf_t.0* %printf_args1, i32 0, i32 1 store i64 %13, i64* %14, align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output3 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.0*, i64)*)(i8* %0, i64 %pseudo2, i64 4294967295, %printf_t.0* %printf_args1, i64 16) %15 = bitcast %printf_t.0* %printf_args1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15) %16 = bitcast %printf_t.1* %printf_args4 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) %17 = bitcast %printf_t.1* %printf_args4 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %17, i8 0, i64 16, i1 false) %18 = getelementptr %printf_t.1, %printf_t.1* %printf_args4, i32 0, i32 0 store i64 2, i64* %18, align 8 %19 = load i64, i64* %"$x", align 8 %20 = sub i64 %19, 1 store i64 %20, i64* %"$x", align 8 %21 = getelementptr %printf_t.1, %printf_t.1* %printf_args4, i32 0, i32 1 store i64 %19, i64* %21, align 8 %pseudo5 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output6 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.1*, i64)*)(i8* %0, i64 %pseudo5, i64 4294967295, %printf_t.1* %printf_args4, i64 16) %22 = bitcast %printf_t.1* %printf_args4 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %22) %23 = bitcast %printf_t.2* %printf_args7 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %23) %24 = bitcast %printf_t.2* %printf_args7 to i8* call void @llvm.memset.p0i8.i64(i8* align 1 %24, i8 0, i64 16, i1 false) %25 = getelementptr %printf_t.2, %printf_t.2* %printf_args7, i32 0, i32 0 store i64 3, i64* %25, align 8 %26 = load i64, i64* %"$x", align 8 %27 = sub i64 %26, 1 store i64 %27, i64* %"$x", align 8 %28 = getelementptr %printf_t.2, %printf_t.2* %printf_args7, i32 0, i32 1 store i64 %27, i64* %28, align 8 %pseudo8 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output9 = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t.2*, i64)*)(i8* %0, i64 %pseudo8, i64 4294967295, %printf_t.2* %printf_args7, i64 16) %29 = bitcast %printf_t.2* %printf_args7 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %29) ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } bpftrace-0.14.0/tests/codegen/llvm/while_loop_no_unroll.ll000066400000000000000000000041661413460502400236710ustar00rootroot00000000000000; ModuleID = 'bpftrace' source_filename = "bpftrace" target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"interval:s:1"(i8* %0) section "s_interval:s:1_1" { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %"$a" = alloca i64, align 8 %1 = bitcast i64* %"$a" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) store i64 0, i64* %"$a", align 8 store i64 0, i64* %"$a", align 8 br label %while_cond while_cond: ; preds = %while_body, %entry %2 = load i64, i64* %"$a", align 8 %3 = icmp sle i64 %2, 10 %4 = zext i1 %3 to i64 %true_cond = icmp ne i64 %4, 0 br i1 %true_cond, label %while_body, label %while_end, !llvm.loop !0 while_body: ; preds = %while_cond %5 = load i64, i64* %"$a", align 8 %6 = add i64 %5, 1 store i64 %6, i64* %"$a", align 8 %7 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@_key", align 8 %8 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) store i64 %5, i64* %"@_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@_key", i64* %"@_val", i64 0) %9 = bitcast i64* %"@_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) br label %while_cond while_end: ; preds = %while_cond ret i64 0 } ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { argmemonly nofree nosync nounwind willreturn } !0 = distinct !{!0, !1} !1 = !{!"llvm.loop.unroll.disable"} bpftrace-0.14.0/tests/codegen/logical_and.cpp000066400000000000000000000003711413460502400210700ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_and) { test("kprobe:f { @x = pid != 1234 && pid != 1235 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/logical_and_or_different_type.cpp000066400000000000000000000006371413460502400246640ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_and_or_different_type) { test("struct Foo { int m; }" "BEGIN" "{" " $foo = *(struct Foo*)arg0;" " printf(\"%d %d %d %d\", $foo.m && 0, 1 && $foo.m, $foo.m || 0, 0 || " "$foo.m)" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/logical_not.cpp000066400000000000000000000003411413460502400211230ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_not) { test("BEGIN { @x = !10; @y = !0; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/logical_or.cpp000066400000000000000000000003701413460502400207450ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_or) { test("kprobe:f { @x = pid == 1234 || pid == 1235 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/macro_definition.cpp000066400000000000000000000003621413460502400221450ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, macro_definition) { test("#define FOO 100\nk:f { @ = FOO }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_assign_array.cpp000066400000000000000000000005371413460502400221570ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_assign_array) { test("struct Foo { int arr[4]; }" "kprobe:f" "{" " @x[0] = ((struct Foo *)arg0)->arr;" " $var = @x[0][0]; " "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_assign_int.cpp000066400000000000000000000003441413460502400216270ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_assign_int) { test("kprobe:f { @x = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_assign_string.cpp000066400000000000000000000003561413460502400223460ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_assign_string) { test("kprobe:f { @x = \"blah\"; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_increment_decrement.cpp000066400000000000000000000004031413460502400234770ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_increment_decrement) { test("BEGIN { @x = 10; @x++; ++@x; @x--; --@x; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_key_array.cpp000066400000000000000000000005001413460502400214510ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_array) { test("struct Foo { int arr[4]; }" "kprobe:f" "{" " @x[((struct Foo *)arg0)->arr] = 44;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_key_int.cpp000066400000000000000000000003531413460502400211330ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_int) { test("kprobe:f { @x[11,22,33] = 44 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_key_probe.cpp000066400000000000000000000005741413460502400214550ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_probe) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "tracepoint:sched:sched_one,tracepoint:sched:sched_two { @x[probe] = " "@x[probe] + 1 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_key_string.cpp000066400000000000000000000003621413460502400216470ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_string) { test("kprobe:f { @x[\"a\", \"b\"] = 44 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/map_key_struct.cpp000066400000000000000000000004701413460502400216650ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_struct) { test("struct Foo { int x; }" "kprobe:f" "{" " @x[*((struct Foo *)arg0)] = 44;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/multiple_identical_probes.cpp000066400000000000000000000003721413460502400240560ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, multiple_identical_probes) { test("kprobe:f { 1; } kprobe:f { 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/nested_array_struct.cpp000066400000000000000000000006111413460502400227150ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, nested_array_struct) { test("struct Bar { int x; } struct Foo { struct Bar bar[2][2]; }" "kprobe:f" "{" " @bar[42] = ((struct Foo *)arg0)->bar;" " @ = @bar[42][0][1].x;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/nested_while_loop.cpp000066400000000000000000000005241413460502400223370ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, nested_while_loop) { test(R"PROG( i:s:1 { $i=1; while ($i <= 100) { $j=0; $i++; while ($j <= 100 ) { @++; $j++; } } } )PROG", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/optional_positional_parameter.cpp000066400000000000000000000003661413460502400247660ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, optional_positional_parameter) { test("BEGIN { @x = $1; @y = str($2) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/pointer_add_int.cpp000066400000000000000000000003621413460502400217760ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pointer_add_int) { test("kprobe:f { $v = (int16*)1000; $v += 10; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/pointer_inc_map.cpp000066400000000000000000000003531413460502400220020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pointer_inc_map) { test("kprobe:f { @ = (int16*)1000; @++ }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/pointer_inc_var.cpp000066400000000000000000000003551413460502400220170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pointer_inc_var) { test("kprobe:f { $v = (int16*)1000; $v++ }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/pred_binop.cpp000066400000000000000000000003571413460502400207610ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pred_binop) { test("kprobe:f / pid == 1234 / { @x = 1 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/ptr_to_ptr.cpp000066400000000000000000000004311413460502400210250ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ptr_to_ptr) { test( R"PROG(kprobe:f { $pp = (int32 **)0; printf("%d\n", **kptr($pp)); })PROG", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/regression.cpp000066400000000000000000000015021413460502400210110ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::_; TEST(codegen, regression_957) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str("t:sched:sched_one* { cat(\"%s\", probe); }"), 0); bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; ast::CodegenLLVM codegen(driver.root_, *bpftrace); codegen.compile(); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/runtime_error_check.cpp000066400000000000000000000007361413460502400226720ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, runtime_error_check) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "kprobe:f { @++; }", NAME); } TEST(codegen, runtime_error_check_lookup) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 2; test(*bpftrace, "kprobe:f { @++; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/string_equal_comparison.cpp000066400000000000000000000004251413460502400235630ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, string_equal_comparison) { test("kretprobe:vfs_read /comm == \"sshd\"/ { @[comm] = count(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/string_not_equal_comparison.cpp000066400000000000000000000004311413460502400244400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, string_not_equal_comparison) { test("kretprobe:vfs_read /comm != \"sshd\"/ { @[comm] = count(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/string_propagation.cpp000066400000000000000000000003571413460502400225510ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, string_propagation) { test("kprobe:f { @x = \"asdf\"; @y = @x }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/strncmp.cpp000066400000000000000000000005721413460502400203250ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, strncmp) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "t:file:filename /str(args->filename) == comm/ { @=1 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_char.cpp000066400000000000000000000010061413460502400211510ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_char) { test("struct Foo { char x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { char x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_integer_ptr.cpp000066400000000000000000000010171413460502400225600ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_integer_ptr) { test("struct Foo { int *x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = *$foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { int *x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = *$foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_integers.cpp000066400000000000000000000010101413460502400220470ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_integers) { test("struct Foo { int x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { int x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_long.cpp000066400000000000000000000010061413460502400211730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_long) { test("struct Foo { long x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { long x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_nested_struct_anon.cpp000066400000000000000000000010721413460502400241400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_nested_struct_anon) { test("struct Foo { struct { int x; } bar; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.bar.x;" "}", std::string(NAME) + "_1"); test("struct Foo { struct { int x; } bar; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->bar.x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_nested_struct_named.cpp000066400000000000000000000011311413460502400242650ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_nested_struct_named) { test("struct Bar { int x; } struct Foo { struct Bar bar; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.bar.x;" "}", std::string(NAME) + "_1"); test("struct Bar { int x; } struct Foo { struct Bar bar; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->bar.x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_nested_struct_ptr_named.cpp000066400000000000000000000011411413460502400251530ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_nested_struct_ptr_named) { test("struct Bar { int x; } struct Foo { struct Bar *bar; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.bar->x;" "}", std::string(NAME) + "_1"); test("struct Bar { int x; } struct Foo { struct Bar *bar; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->bar->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_save.cpp000066400000000000000000000007401413460502400211760ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_save) { test("struct Foo { int x, y, z; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" "}", std::string(NAME) + "_1"); test("struct Foo { int x, y, z; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_save_nested.cpp000066400000000000000000000006231413460502400225400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_save_nested) { test("struct Foo { int m; struct { int x; int y; } bar; int n; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" " @bar = @foo.bar;" " @x = @foo.bar.x;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_save_string.cpp000066400000000000000000000005311413460502400225620ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_save_string) { test("struct Foo { char str[32]; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" " @str = @foo.str;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_semicolon.cpp000066400000000000000000000007541413460502400222350ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_semicolon) { test("struct Foo { int x, y; char *str; };" "k:f" "{" " printf(\"%s\\n\", ustack);" "}", std::string(NAME) + "_1"); test("struct Foo { int x, y; char *str; }" "k:f" "{" " printf(\"%s\\n\", ustack);" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_short.cpp000066400000000000000000000010111413460502400213670ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_short) { test("struct Foo { short x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { short x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_string_array.cpp000066400000000000000000000010461413460502400227440ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_string_array) { test("struct Foo { char str[32]; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @mystr = $foo.str;" "}", std::string(NAME) + "_1"); test("struct Foo { char str[32]; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @mystr = $foo->str;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/struct_string_ptr.cpp000066400000000000000000000005341413460502400224340ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_string_ptr) { test("struct Foo { char *str; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @mystr = str($foo->str);" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/ternary_int.cpp000066400000000000000000000003531413460502400211720ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_int) { test("kprobe:f { @x = pid < 10000 ? 1 : 2; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/ternary_none.cpp000066400000000000000000000003741413460502400213420ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_none) { test("kprobe:f { pid < 10000 ? printf(\"hello\") : exit(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/ternary_str.cpp000066400000000000000000000003651413460502400212130ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_str) { test("kprobe:f { @x = pid < 10000 ? \"lo\" : \"hi\"; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/tuple.cpp000066400000000000000000000003471413460502400177700ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, tuple) { test(R"_(k:f { @t = (1, 2, "str"); })_", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/tuple_array_struct.cpp000066400000000000000000000005641413460502400225730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, tuple_array_struct) { test("struct Foo { char c; int x; } struct Bar { int y[4]; }" "kprobe:f" "{" " @t = (*((struct Foo *)arg0), ((struct Bar *)arg1)->y);" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/unroll.cpp000066400000000000000000000003471413460502400201520ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, unroll) { test("BEGIN { @i = 0; unroll(5) { @i += 1 } }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/unroll_async_id.cpp000066400000000000000000000003701413460502400220170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, unroll_async_id) { test(R"(BEGIN { @i = 0; unroll(5) { printf("hi") } })", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/variable.cpp000066400000000000000000000003601413460502400204170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, variable) { test("kprobe:f { $var = comm; @x = $var; @y = $var }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/variable_assign_array.cpp000066400000000000000000000005401413460502400231610ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, variable_assign_array) { test("struct Foo { int arr[4]; }" "kprobe:f" "{" " $var = ((struct Foo *)arg0)->arr;" " @x = $var[0]; " "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/variable_increment_decrement.cpp000066400000000000000000000005211413460502400245100ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, variable_increment_decrement) { test("BEGIN { $x = 10; printf(\"%d\", $x++); printf(\"%d\", ++$x); " "printf(\"%d\", $x--); printf(\"%d\", --$x); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/codegen/while_loop_no_unroll.cpp000066400000000000000000000005461413460502400230700ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { // Trip counts under 10 are usually unrolled automatically. // // This tests that the loop isn't unrolled. TEST(codegen, while_loop_no_unroll) { test("i:s:1 { $a = 0; while ($a <= 10) { @=$a++; }}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/data/000077500000000000000000000000001413460502400154345ustar00rootroot00000000000000bpftrace-0.14.0/tests/data/btf_data.h000066400000000000000000000146521413460502400173610ustar00rootroot00000000000000#pragma once // The data consists of following source file objects: // // struct Foo1 { // int a; // char b; // long c; // }; // // struct Foo2 { // int a; // union { // struct Foo1 f; // struct { // char g; // }; // }; // }; // // struct Foo3 { // struct Foo1 *foo1; // const volatile struct Foo2 * restrict foo2; // }; // // struct Foo3 foo3; // // struct Foo3 *func_1(int a, struct Foo1 *foo1, struct Foo2 *foo2) // { // return 0; // } // // struct Foo3 *func_2(int a, int b, struct Foo1 *foo1) // { // return 0; // } // // struct Foo3 *func_3(int a, int b, struct Foo1 *foo1) // { // return 0; // } // // struct task_struct { // int pid; // int pgid; // }; // // struct file { // int ino; // }; // // struct bpf_iter__task { // struct task_struct *task; // }; // // struct bpf_iter__task_file { // struct task_struct *task; // struct file *file; // }; // // int main(void) // { // struct bpf_iter__task iter_task; // struct bpf_iter__task_file iter_task_file; // // func_1(0, 0, 0); // return 0; // } // generated by: // % cat a.c // [... the above definitions ...] // struct Foo3 f3; // % clang -target bpf -g -c a.c // % llvm-objcopy --dump-section .BTF=a.btf a.o // % hexdump -ve '13/1 "0x%02x, " "\n"' a.btf // // or by: // // $ cat a.c // [... the above definitions ...] // $ gcc -o btf -g btf.c // $ pahole -J btf # v1.17 and higher // $ objcopy --dump-section .BTF=data btf // $ xxd -i data > data.h unsigned char btf_data[] = { 0x9f, 0xeb, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x02, 0x00, 0x00, 0x4c, 0x02, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x08, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0d, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0d, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x46, 0x6f, 0x6f, 0x31, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x69, 0x6e, 0x74, 0x00, 0x63, 0x68, 0x61, 0x72, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x67, 0x00, 0x66, 0x00, 0x46, 0x6f, 0x6f, 0x32, 0x00, 0x46, 0x6f, 0x6f, 0x33, 0x00, 0x66, 0x6f, 0x6f, 0x31, 0x00, 0x66, 0x6f, 0x6f, 0x32, 0x00, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x00, 0x70, 0x69, 0x64, 0x00, 0x70, 0x67, 0x69, 0x64, 0x00, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x69, 0x6e, 0x6f, 0x00, 0x62, 0x70, 0x66, 0x5f, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x00, 0x74, 0x61, 0x73, 0x6b, 0x00, 0x62, 0x70, 0x66, 0x5f, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x33, 0x00, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x32, 0x00, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x31, 0x00 }; unsigned int btf_data_len = sizeof(btf_data) / sizeof(btf_data[0]); // List of functions from the above data unsigned char func_list[] = "func_1\n" "func_2\n" "func_3\n"; unsigned int func_list_len = sizeof(func_list) / sizeof(func_list[0]); bpftrace-0.14.0/tests/data/data_source.c000066400000000000000000000010151413460502400200660ustar00rootroot00000000000000struct Foo1 { int a; char b; long c; }; struct Foo2 { int a; union { struct Foo1 f; struct { char g; }; }; }; struct Foo3 { struct Foo1 *foo1; const volatile struct Foo2 *restrict foo2; }; struct Foo3 foo3; struct Foo3 *func_1(int a, struct Foo1 *foo1, struct Foo2 *foo2) { return 0; } struct Foo3 *func_2(int a, int *b, struct Foo1 *foo1) { return 0; } struct Foo3 *func_3(int a, int *b, struct Foo1 *foo1) { return 0; } int main(void) { func_1(0, 0, 0); return 0; } bpftrace-0.14.0/tests/data/dwarf_data.h.in000066400000000000000000000000331413460502400203020ustar00rootroot00000000000000#pragma once ${DWARF_DATA}bpftrace-0.14.0/tests/data/generate_debuginfo_data.cmake000066400000000000000000000011421413460502400232410ustar00rootroot00000000000000# Generates dwarf_data.h that stores DWARF from data_source.c in the form of # a byte array execute_process(COMMAND gcc -g -o data_source.o data_source.c WORKING_DIRECTORY ${DATA_SOURCE_DIR}) execute_process(COMMAND strip --only-keep-debug -o dwarf_data data_source.o WORKING_DIRECTORY ${DATA_SOURCE_DIR}) execute_process(COMMAND xxd -i dwarf_data OUTPUT_VARIABLE DWARF_DATA WORKING_DIRECTORY ${DATA_SOURCE_DIR}) configure_file(${DATA_SOURCE_DIR}/dwarf_data.h.in ${DATA_SOURCE_DIR}/dwarf_data.h) # Cleanup temporary files file(REMOVE ${DATA_SOURCE_DIR}/data_source.o ${DATA_SOURCE_DIR}/dwarf_data)bpftrace-0.14.0/tests/dwarf_common.h000066400000000000000000000011011413460502400173400ustar00rootroot00000000000000#pragma once #include "data/dwarf_data.h" #include class test_dwarf : public ::testing::Test { protected: void SetUp() override { char *bin = strdup("/tmp/dwarf_dataXXXXXX"); int fd = mkstemp(bin); if (fd < 0) return; fchmod(fd, S_IRUSR | S_IWUSR | S_IXUSR); if (write(fd, dwarf_data, dwarf_data_len) != dwarf_data_len) { close(fd); std::remove(bin); return; } close(fd); bin_ = bin; } void TearDown() override { if (bin_) std::remove(bin_); } public: char *bin_ = nullptr; };bpftrace-0.14.0/tests/log.cpp000066400000000000000000000047651413460502400160240ustar00rootroot00000000000000#include "log.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include namespace bpftrace { namespace test { namespace log { TEST(LogStream, basic) { std::ostringstream ss; const std::string content_1 = "hello world"; const std::string content_2 = "some messages 100###**"; // clang-format off LOG(DEBUG, ss) << content_1; std::string file = __FILE__; int line = __LINE__; // clang-format on std::string prefix = "[" + file + ":" + std::to_string(line) + "] "; EXPECT_EQ(ss.str(), "DEBUG: " + prefix + content_1 + "\n"); ss.str({}); LOG(WARNING, ss) << content_1 << content_2; EXPECT_EQ(ss.str(), "WARNING: " + content_1 + content_2 + "\n"); ss.str({}); LOG(ERROR, ss) << content_1 << content_2 << content_1 << content_2 << "\n" << content_1 << content_2 << content_1 << content_2 << "\n"; const std::string content_long = content_1 + content_2 + content_1 + content_2; EXPECT_EQ(ss.str(), "ERROR: " + content_long + "\n" + content_long + "\n\n"); ss.str({}); // test macro with 1 argument auto cerr_buf = std::cerr.rdbuf(ss.rdbuf()); LOG(INFO) << content_1 << content_2; EXPECT_EQ(ss.str(), "INFO: " + content_1 + content_2 + "\n"); std::cerr.rdbuf(cerr_buf); } TEST(LogStream, with_location) { std::ostringstream ss; const std::string filename = "stdin"; const std::string source = "i:s:1 { print(1, 2); }"; const std::string expected = "stdin:1:9-20: ERROR: Non-map print() only takes 1 argument, 2 " "found\ni:s:1 { print(1, 2); }\n ~~~~~~~~~~~\n"; bpftrace::location loc(bpftrace::position(nullptr, 1, 9), bpftrace::position(nullptr, 1, 20)); Log::get().set_source(filename, source); LOG(ERROR, loc, ss) << "Non-map print() only takes 1 argument, 2 found"; EXPECT_EQ(ss.str(), expected); } TEST(Log, disable_log_type) { std::ostringstream ss; const std::string content = "This is the warning message"; LOG(WARNING, ss) << content; EXPECT_EQ(ss.str(), "WARNING: " + content + "\n"); ss.str({}); Log::get().disable(LogType::WARNING); LOG(WARNING, ss) << content; EXPECT_EQ(ss.str(), ""); // make sure other log types are not affected LOG(ERROR, ss) << content; EXPECT_EQ(ss.str(), "ERROR: " + content + "\n"); ss.str({}); Log::get().enable(LogType::WARNING); LOG(WARNING, ss) << content; EXPECT_EQ(ss.str(), "WARNING: " + content + "\n"); ss.str({}); } } // namespace log } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/main.cpp000066400000000000000000000007411413460502400161550ustar00rootroot00000000000000#include "gtest/gtest.h" class ThrowListener : public testing::EmptyTestEventListener { void OnTestPartResult(const testing::TestPartResult& result) override { if (result.type() == testing::TestPartResult::kFatalFailure) { throw testing::AssertionException(result); } } }; int main(int argc, char *argv[]) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); return RUN_ALL_TESTS(); } bpftrace-0.14.0/tests/memleak-tests.sh000077500000000000000000000024021413460502400176330ustar00rootroot00000000000000#!/bin/bash if [[ $EUID -ne 0 ]]; then >&2 echo "Must be run as root" exit 1 fi set -e; pushd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 BPFTRACE_ASAN=${BPFTRACE_ASAN:-../src/bpftrace} if ! nm $BPFTRACE_ASAN | grep -q __asan; then >&2 echo "WARNING: bpftrace seems to be compiled without -fsanitize=address," >&2 echo "results may be incorrect (make sure to use -DBUILD_ASAN=On with CMake)" fi if tty -s; then RED=`tput setaf 1` GREEN=`tput setaf 2` NC=`tput sgr 0` else RED= GREEN= NC= fi # Add new testcases here tests=( '"BEGIN { exit(); }"' $'"#include \n BEGIN { \$x = ((struct sk_buff *)curtask)->data_len; exit(); }"' ) echo "${GREEN}[==========]${NC} Running ${#tests[@]} tests" result=0 for tst in "${tests[@]}"; do echo "${GREEN}[ RUN ]${NC} bpftrace -e $tst" export ASAN_OPTIONS="alloc_dealloc_mismatch=0" if eval $BPFTRACE_ASAN -e "$tst" > /dev/null 2>&1 ; then echo "${GREEN}[ OK ]" else echo "${RED}[ MEMLEAK ]" result=1 fi done echo "${GREEN}[==========]" if [ $result -eq 0 ]; then echo "${GREEN}[ PASSED ]${NC} All tests were succesful" else echo "${RED}[ FAILED ]${NC} Memory leaks detected" fi exit $result bpftrace-0.14.0/tests/mocks.cpp000066400000000000000000000125131413460502400163450ustar00rootroot00000000000000#include "mocks.h" namespace bpftrace { namespace test { using ::testing::_; using ::testing::NiceMock; using ::testing::Return; using ::testing::StrictMock; void setup_mock_probe_matcher(MockProbeMatcher &matcher) { ON_CALL(matcher, get_symbols_from_file( "/sys/kernel/debug/tracing/available_filter_functions")) .WillByDefault([](const std::string &) { std::string ksyms = "SyS_read\n" "sys_read\n" "sys_write\n" "my_one\n" "my_two\n" "func_in_mod [kernel_mod]\n"; auto myval = std::unique_ptr(new std::istringstream(ksyms)); return myval; }); ON_CALL(matcher, get_symbols_from_file("/sys/kernel/debug/tracing/available_events")) .WillByDefault([](const std::string &) { std::string tracepoints = "sched:sched_one\n" "sched:sched_two\n" "sched:foo\n" "sched_extra:sched_extra\n" "notsched:bar\n" "file:filename\n" "tcp:some_tcp_tp\n"; return std::unique_ptr(new std::istringstream(tracepoints)); }); std::string sh_usyms = "/bin/sh:first_open\n" "/bin/sh:second_open\n" "/bin/sh:open_as_well\n" "/bin/sh:something_else\n" "/bin/sh:_Z11cpp_mangledi\n" "/bin/sh:_Z11cpp_mangledv\n"; std::string bash_usyms = "/bin/bash:first_open\n"; ON_CALL(matcher, get_func_symbols_from_file("/bin/sh")) .WillByDefault([sh_usyms](const std::string &) { return std::unique_ptr(new std::istringstream(sh_usyms)); }); ON_CALL(matcher, get_func_symbols_from_file("/bin/*sh")) .WillByDefault([sh_usyms, bash_usyms](const std::string &) { return std::unique_ptr( new std::istringstream(sh_usyms + bash_usyms)); }); std::string sh_usdts = "/bin/sh:prov1:tp1\n" "/bin/sh:prov1:tp2\n" "/bin/sh:prov2:tp\n" "/bin/sh:prov2:notatp\n" "/bin/sh:nahprov:tp\n"; std::string bash_usdts = "/bin/bash:prov1:tp3"; ON_CALL(matcher, get_symbols_from_usdt(_, "/bin/sh")) .WillByDefault([sh_usdts](int, const std::string &) { return std::unique_ptr(new std::istringstream(sh_usdts)); }); ON_CALL(matcher, get_symbols_from_usdt(_, "/bin/*sh")) .WillByDefault([sh_usdts, bash_usdts](int, const std::string &) { return std::unique_ptr( new std::istringstream(sh_usdts + bash_usdts)); }); } void setup_mock_bpftrace(MockBPFtrace &bpftrace) { // Fill in some default tracepoint struct definitions bpftrace.structs.Add("struct _tracepoint_sched_sched_one", 8); bpftrace.structs.Lookup("struct _tracepoint_sched_sched_one") .lock() ->AddField("common_field", CreateUInt64(), 8, false, {}, false); bpftrace.structs.Add("struct _tracepoint_sched_sched_two", 8); bpftrace.structs.Lookup("struct _tracepoint_sched_sched_two") .lock() ->AddField("common_field", CreateUInt64(), 16, // different offset than // sched_one.common_field false, {}, false); bpftrace.structs.Add("struct _tracepoint_sched_extra_sched_extra", 8); bpftrace.structs.Lookup("struct _tracepoint_sched_extra_sched_extra") .lock() ->AddField("common_field", CreateUInt64(), 24, // different offset than // sched_(one|two).common_field false, {}, false); bpftrace.structs.Add("struct _tracepoint_tcp_some_tcp_tp", 16); bpftrace.structs.Lookup("struct _tracepoint_tcp_some_tcp_tp") .lock() ->AddField( "saddr_v6", CreateArray(16, CreateUInt(8)), 8, false, {}, false); auto ptr_type = CreatePointer(CreateInt8()); bpftrace.structs.Add("struct _tracepoint_file_filename", 8); bpftrace.structs.Lookup("struct _tracepoint_file_filename") .lock() ->AddField("common_field", CreateUInt64(), 0, false, {}, false); bpftrace.structs.Lookup("struct _tracepoint_file_filename") .lock() ->AddField("filename", ptr_type, 8, false, {}, false); } std::unique_ptr get_mock_bpftrace() { auto bpftrace = std::make_unique>(); setup_mock_bpftrace(*bpftrace); auto probe_matcher = std::make_unique>( bpftrace.get()); setup_mock_probe_matcher(*probe_matcher); bpftrace->set_mock_probe_matcher(std::move(probe_matcher)); return bpftrace; } std::unique_ptr get_strict_mock_bpftrace() { auto bpftrace = std::make_unique>(); setup_mock_bpftrace(*bpftrace); auto probe_matcher = std::make_unique>( bpftrace.get()); setup_mock_probe_matcher(*probe_matcher); bpftrace->set_mock_probe_matcher(std::move(probe_matcher)); return bpftrace; } } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/mocks.h000066400000000000000000000070761413460502400160220ustar00rootroot00000000000000#pragma once #include "gmock/gmock.h" #include "bpffeature.h" #include "bpftrace.h" #include "child.h" #include "probe_matcher.h" #include "procmon.h" namespace bpftrace { namespace test { class MockProbeMatcher : public ProbeMatcher { public: MockProbeMatcher(BPFtrace *bpftrace) : ProbeMatcher(bpftrace) { } #pragma GCC diagnostic push #ifdef __clang__ #pragma GCC diagnostic ignored "-Winconsistent-missing-override" #endif MOCK_CONST_METHOD1(get_symbols_from_file, std::unique_ptr(const std::string &path)); MOCK_CONST_METHOD2(get_symbols_from_usdt, std::unique_ptr(int pid, const std::string &target)); MOCK_CONST_METHOD1(get_func_symbols_from_file, std::unique_ptr(const std::string &path)); #pragma GCC diagnostic pop }; class MockBPFtrace : public BPFtrace { public: std::vector get_probes() { return resources.probes; } std::vector get_special_probes() { return resources.special_probes; } int resolve_uname(const std::string &name, struct symbol *sym, const std::string &path) const override { (void)path; sym->name = name; if (name == "cpp_mangled" || name == "cpp_mangled(int)") { return -1; } else if (name[0] >= 'A' && name[0] <= 'z') { sym->address = 12345; sym->size = 4; } else { auto fields = split_string(name, '_'); sym->address = std::stoull(fields.at(0)); sym->size = std::stoull(fields.at(1)); } return 0; } bool is_traceable_func( const std::string &__attribute__((unused))) const override { return true; } void set_mock_probe_matcher(std::unique_ptr probe_matcher) { probe_matcher_ = std::move(probe_matcher); mock_probe_matcher = dynamic_cast(probe_matcher_.get()); } MockProbeMatcher *mock_probe_matcher; }; std::unique_ptr get_mock_bpftrace(); std::unique_ptr get_strict_mock_bpftrace(); class MockBPFfeature : public BPFfeature { public: MockBPFfeature(bool has_features = true) { has_send_signal_ = std::make_optional(has_features); has_get_current_cgroup_id_ = std::make_optional(has_features); has_override_return_ = std::make_optional(has_features); prog_kfunc_ = std::make_optional(has_features); prog_iter_task_ = std::make_optional(has_features); prog_iter_task_file_ = std::make_optional(has_features); has_loop_ = std::make_optional(has_features); has_probe_read_kernel_ = std::make_optional(has_features); has_features_ = has_features; has_d_path_ = std::make_optional(has_features); has_ktime_get_boot_ns_ = std::make_optional(has_features); }; bool has_features_; }; class MockChildProc : public ChildProcBase { public: MockChildProc(std::string cmd __attribute__((unused))) { child_pid_ = 1337; }; ~MockChildProc(){}; void terminate(bool force __attribute__((unused)) = false) override{}; bool is_alive() override { return true; }; void resume(void) override{}; void run(bool pause = false) override { (void)pause; }; }; class MockProcMon : public ProcMonBase { public: MockProcMon(pid_t pid) { pid_ = pid; } ~MockProcMon() override = default; bool is_alive(void) override { if (pid_ > 0) return true; else return false; } }; } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/parser.cpp000066400000000000000000001253531413460502400165340ustar00rootroot00000000000000#include #include #include "ast/passes/printer.h" #include "driver.h" #include "gtest/gtest.h" namespace bpftrace { namespace test { namespace parser { using Printer = ast::Printer; void test_parse_failure(BPFtrace &bpftrace, const std::string &input) { std::stringstream out; Driver driver(bpftrace, out); ASSERT_EQ(driver.parse_str(input), 1); } void test_parse_failure(const std::string &input) { BPFtrace bpftrace; test_parse_failure(bpftrace, input); } void test(BPFtrace &bpftrace, const std::string &input, const std::string &output) { Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(input), 0); std::ostringstream out; Printer printer(out); printer.print(driver.root_); EXPECT_EQ(output, out.str()); } void test(const std::string &input, const std::string &output) { BPFtrace bpftrace; test(bpftrace, input, output); } TEST(Parser, builtin_variables) { test("kprobe:f { pid }", "Program\n kprobe:f\n builtin: pid\n"); test("kprobe:f { tid }", "Program\n kprobe:f\n builtin: tid\n"); test("kprobe:f { cgroup }", "Program\n kprobe:f\n builtin: cgroup\n"); test("kprobe:f { uid }", "Program\n kprobe:f\n builtin: uid\n"); test("kprobe:f { username }", "Program\n kprobe:f\n builtin: username\n"); 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 { elapsed }", "Program\n kprobe:f\n builtin: elapsed\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 { rand }", "Program\n kprobe:f\n builtin: rand\n"); test("kprobe:f { ctx }", "Program\n kprobe:f\n builtin: ctx\n"); test("kprobe:f { comm }", "Program\n kprobe:f\n builtin: comm\n"); test("kprobe:f { kstack }", "Program\n kprobe:f\n builtin: kstack\n"); test("kprobe:f { ustack }", "Program\n kprobe:f\n builtin: ustack\n"); test("kprobe:f { arg0 }", "Program\n kprobe:f\n builtin: arg0\n"); test("kprobe:f { sarg0 }", "Program\n kprobe:f\n builtin: sarg0\n"); test("kprobe:f { retval }", "Program\n kprobe:f\n builtin: retval\n"); test("kprobe:f { func }", "Program\n kprobe:f\n builtin: func\n"); test("kprobe:f { probe }", "Program\n kprobe:f\n builtin: probe\n"); test("kprobe:f { args }", "Program\n kprobe:f\n builtin: args\n"); } TEST(Parser, positional_param) { test("kprobe:f { $1 }", "Program\n kprobe:f\n param: $1\n"); test_parse_failure("kprobe:f { $0 }"); } TEST(Parser, positional_param_count) { test("kprobe:f { $# }", "Program\n kprobe:f\n param: $#\n"); } TEST(Parser, positional_param_attachpoint) { BPFtrace bpftrace; bpftrace.add_param("foo"); bpftrace.add_param("bar"); bpftrace.add_param("baz"); test(bpftrace, "kprobe:$1 { 1 }", R"PROG(Program kprobe:foo int: 1 )PROG"); test(bpftrace, R"PROG(kprobe:$1"here" { 1 })PROG", R"PROG(Program kprobe:foohere int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:zzzzzzz:$2 { 1 })PROG", R"PROG(Program uprobe:zzzzzzz:bar int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:$1:$2 { 1 })PROG", R"PROG(Program uprobe:foo:bar int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:$2:$1 { 1 })PROG", R"PROG(Program uprobe:bar:foo int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:"zz"$2"zz":"aa"$1 { 1 })PROG", R"PROG(Program uprobe:zzbarzz:aafoo int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:$2:"aa"$1"aa" { 1 })PROG", R"PROG(Program uprobe:bar:aafooaa int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:"$1":$2 { 1 })PROG", R"PROG(Program uprobe:$1:bar int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:aa$1:$2 { 1 })PROG", R"PROG(Program uprobe:aafoo:bar int: 1 )PROG"); test_parse_failure(bpftrace, R"PROG(uprobe:$1a" { 1 })PROG"); test_parse_failure(bpftrace, R"PROG(uprobe:$a" { 1 })PROG"); test_parse_failure(bpftrace, R"PROG(uprobe:$-1" { 1 })PROG"); test_parse_failure(bpftrace, R"PROG(uprobe:$999999999999999999999999" { 1 })PROG"); } TEST(Parser, comment) { test("kprobe:f { /*** ***/0; }", "Program\n kprobe:f\n int: 0\n"); } TEST(Parser, map_assign) { test("kprobe:sys_open { @x = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " int: 1\n"); test("kprobe:sys_open { @x = @y; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " map: @y\n"); test("kprobe:sys_open { @x = arg0; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " builtin: arg0\n"); test("kprobe:sys_open { @x = count(); }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " call: count\n"); test("kprobe:sys_read { @x = sum(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: sum\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = min(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: min\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = max(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: max\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = avg(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: avg\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = stats(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: stats\n" " builtin: arg2\n"); test("kprobe:sys_open { @x = \"mystring\" }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " string: mystring\n"); test("kprobe:sys_open { @x = $myvar; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " variable: $myvar\n"); } TEST(Parser, variable_assign) { test("kprobe:sys_open { $x = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " variable: $x\n" " int: 1\n"); test("kprobe:sys_open { $x = -1; }", "Program\n" " kprobe:sys_open\n" " =\n" " variable: $x\n" " int: -1\n"); char in_cstr[128]; char out_cstr[128]; snprintf(in_cstr, sizeof(in_cstr), "kprobe:sys_open { $x = %ld; }", LONG_MIN); snprintf(out_cstr, sizeof(out_cstr), "Program\n" " kprobe:sys_open\n" " =\n" " variable: $x\n" " int: %ld\n", LONG_MIN); test(std::string(in_cstr), std::string(out_cstr)); } TEST(semantic_analyser, compound_variable_assignments) { test("kprobe:f { $a = 0; $a <<= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " <<\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a >>= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " >>\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a += 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " +\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a -= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " -\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a *= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " *\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a /= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " /\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a %= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " %\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a &= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " &\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a |= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " |\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a ^= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " ^\n" " variable: $a\n" " int: 1\n"); } TEST(Parser, compound_map_assignments) { test("kprobe:f { @a <<= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " <<\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a >>= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " >>\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a += 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " +\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a -= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " -\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a *= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " *\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a /= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " /\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a %= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " %\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a &= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " &\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a |= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " |\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a ^= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " ^\n" " map: @a\n" " int: 1\n"); } TEST(Parser, integer_sizes) { test("kprobe:do_nanosleep { $x = 0x12345678; }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " variable: $x\n" " int: 305419896\n"); test("kprobe:do_nanosleep { $x = 0x4444444412345678; }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " variable: $x\n" " int: 4919131752149309048\n"); } TEST(Parser, map_key) { test("kprobe:sys_open { @x[0] = 1; @x[0,1,2] = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " int: 0\n" " int: 1\n" " =\n" " map: @x\n" " int: 0\n" " int: 1\n" " int: 2\n" " int: 1\n"); test("kprobe:sys_open { @x[@a] = 1; @x[@a,@b,@c] = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " map: @a\n" " int: 1\n" " =\n" " map: @x\n" " map: @a\n" " map: @b\n" " map: @c\n" " int: 1\n"); test("kprobe:sys_open { @x[pid] = 1; @x[tid,uid,arg9] = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " builtin: pid\n" " int: 1\n" " =\n" " map: @x\n" " builtin: tid\n" " builtin: uid\n" " builtin: arg9\n" " int: 1\n"); } TEST(Parser, predicate) { test("kprobe:sys_open / @x / { 1; }", "Program\n" " kprobe:sys_open\n" " pred\n" " map: @x\n" " int: 1\n"); } TEST(Parser, predicate_containing_division) { test("kprobe:sys_open /100/25/ { 1; }", "Program\n" " kprobe:sys_open\n" " pred\n" " /\n" " int: 100\n" " int: 25\n" " int: 1\n"); } TEST(Parser, expressions) { test("kprobe:sys_open / 1 <= 2 && (9 - 4 != 5*10 || ~0) || comm == \"string\" /\n" "{\n" " 1;\n" "}", "Program\n" " kprobe:sys_open\n" " pred\n" " ||\n" " &&\n" " <=\n" " int: 1\n" " int: 2\n" " ||\n" " !=\n" " -\n" " int: 9\n" " int: 4\n" " *\n" " int: 5\n" " int: 10\n" " ~\n" " int: 0\n" " ==\n" " builtin: comm\n" " string: string\n" " int: 1\n"); } TEST(Parser, variable_post_increment_decrement) { test("kprobe:sys_open { $x++; }", "Program\n" " kprobe:sys_open\n" " variable: $x\n" " ++\n"); test("kprobe:sys_open { ++$x; }", "Program\n" " kprobe:sys_open\n" " ++\n" " variable: $x\n"); test("kprobe:sys_open { $x--; }", "Program\n" " kprobe:sys_open\n" " variable: $x\n" " --\n"); test("kprobe:sys_open { --$x; }", "Program\n" " kprobe:sys_open\n" " --\n" " variable: $x\n"); } TEST(Parser, map_increment_decrement) { test("kprobe:sys_open { @x++; }", "Program\n" " kprobe:sys_open\n" " map: @x\n" " ++\n"); test("kprobe:sys_open { ++@x; }", "Program\n" " kprobe:sys_open\n" " ++\n" " map: @x\n"); test("kprobe:sys_open { @x--; }", "Program\n" " kprobe:sys_open\n" " map: @x\n" " --\n"); test("kprobe:sys_open { --@x; }", "Program\n" " kprobe:sys_open\n" " --\n" " map: @x\n"); } TEST(Parser, bit_shifting) { test("kprobe:do_nanosleep { @x = 1 << 10 }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " map: @x\n" " <<\n" " int: 1\n" " int: 10\n"); test("kprobe:do_nanosleep { @x = 1024 >> 9 }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " map: @x\n" " >>\n" " int: 1024\n" " int: 9\n"); test("kprobe:do_nanosleep / 2 < 1 >> 8 / { $x = 1 }", "Program\n" " kprobe:do_nanosleep\n" " pred\n" " <\n" " int: 2\n" " >>\n" " int: 1\n" " int: 8\n" " =\n" " variable: $x\n" " int: 1\n"); } TEST(Parser, ternary_int) { test("kprobe:sys_open { @x = pid < 10000 ? 1 : 2 }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " ?:\n" " <\n" " builtin: pid\n" " int: 10000\n" " int: 1\n" " int: 2\n"); } TEST(Parser, if_block) { test("kprobe:sys_open { if (pid > 10000) { printf(\"%d is high\\n\", pid); } }", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " call: printf\n" " string: %d is high\\n\n" " builtin: pid\n"); } TEST(Parser, if_stmt_if) { test("kprobe:sys_open { if (pid > 10000) { printf(\"%d is high\\n\", pid); } @pid = pid; if (pid < 1000) { printf(\"%d is low\\n\", pid); } }", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " call: printf\n" " string: %d is high\\n\n" " builtin: pid\n" " =\n" " map: @pid\n" " builtin: pid\n" " if\n" " <\n" " builtin: pid\n" " int: 1000\n" " then\n" " call: printf\n" " string: %d is low\\n\n" " builtin: pid\n"); } TEST(Parser, if_block_variable) { test("kprobe:sys_open { if (pid > 10000) { $s = 10; } }", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n"); } TEST(Parser, if_else) { test("kprobe:sys_open { if (pid > 10000) { $s = \"a\"; } else { $s= \"b\"; } printf(\"%d is high\\n\", pid, $s); }", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " string: a\n" " else\n" " =\n" " variable: $s\n" " string: b\n" " call: printf\n" " string: %d is high\\n\n" " builtin: pid\n" " variable: $s\n"); } TEST(Parser, if_elseif) { test("kprobe:f { if (pid > 10000) { $s = 10; } else if (pid < 10) { $s = 2; " "} }", "Program\n" " kprobe:f\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n" " else\n" " if\n" " <\n" " builtin: pid\n" " int: 10\n" " then\n" " =\n" " variable: $s\n" " int: 2\n"); } TEST(Parser, if_elseif_else) { test("kprobe:f { if (pid > 10000) { $s = 10; } else if (pid < 10) { $s = 2; " "} else { $s = 1 } }", "Program\n" " kprobe:f\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n" " else\n" " if\n" " <\n" " builtin: pid\n" " int: 10\n" " then\n" " =\n" " variable: $s\n" " int: 2\n" " else\n" " =\n" " variable: $s\n" " int: 1\n"); } TEST(Parser, if_elseif_elseif_else) { test("kprobe:f { if (pid > 10000) { $s = 10; } else if (pid < 10) { $s = 2; " "} else if (pid > 999999) { $s = 0 } else { $s = 1 } }", "Program\n" " kprobe:f\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n" " else\n" " if\n" " <\n" " builtin: pid\n" " int: 10\n" " then\n" " =\n" " variable: $s\n" " int: 2\n" " else\n" " if\n" " >\n" " builtin: pid\n" " int: 999999\n" " then\n" " =\n" " variable: $s\n" " int: 0\n" " else\n" " =\n" " variable: $s\n" " int: 1\n"); } TEST(Parser, unroll) { test("kprobe:sys_open { $i = 0; unroll(5) { printf(\"i: %d\\n\", $i); $i = " "$i + 1; } }", "Program\n" " kprobe:sys_open\n" " =\n" " variable: $i\n" " int: 0\n" " unroll\n" " int: 5\n" " block\n" " call: printf\n" " string: i: %d\\n\n" " variable: $i\n" " =\n" " variable: $i\n" " +\n" " variable: $i\n" " int: 1\n"); } TEST(Parser, ternary_str) { test("kprobe:sys_open { @x = pid < 10000 ? \"lo\" : \"high\" }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " ?:\n" " <\n" " builtin: pid\n" " int: 10000\n" " string: lo\n" " string: high\n"); } TEST(Parser, ternary_nested) { test("kprobe:sys_open { @x = pid < 10000 ? pid < 5000 ? 1 : 2 : 3 }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " ?:\n" " <\n" " builtin: pid\n" " int: 10000\n" " ?:\n" " <\n" " builtin: pid\n" " int: 5000\n" " int: 1\n" " int: 2\n" " int: 3\n"); } TEST(Parser, call) { test("kprobe:sys_open { @x = count(); @y = hist(1,2,3); delete(@x); }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " call: count\n" " =\n" " map: @y\n" " call: hist\n" " int: 1\n" " int: 2\n" " int: 3\n" " call: delete\n" " map: @x\n"); } TEST(Parser, call_unknown_function) { test_parse_failure("kprobe:sys_open { myfunc() }"); test_parse_failure("k:f { probe(); }"); } TEST(Parser, call_builtin) { // Builtins should not be usable as function test_parse_failure("k:f { nsecs(); }"); test_parse_failure("k:f { nsecs (); }"); test_parse_failure("k:f { nsecs(\"abc\"); }"); test_parse_failure("k:f { nsecs(123); }"); test_parse_failure("k:f { probe(\"blah\"); }"); test_parse_failure("k:f { probe(); }"); test_parse_failure("k:f { probe(123); }"); } TEST(Parser, call_kaddr) { test("kprobe:f { @ = kaddr(\"avenrun\") }", "Program\n" " kprobe:f\n" " =\n" " map: @\n" " call: kaddr\n" " string: avenrun\n"); } TEST(Parser, multiple_probes) { test("kprobe:sys_open { 1; } kretprobe:sys_open { 2; }", "Program\n" " kprobe:sys_open\n" " int: 1\n" " kretprobe:sys_open\n" " int: 2\n"); } TEST(Parser, uprobe) { test("uprobe:/my/program:func { 1; }", "Program\n" " uprobe:/my/program:func\n" " int: 1\n"); test("uprobe:/my/go/program:\"pkg.func\u2C51\" { 1; }", "Program\n" " uprobe:/my/go/program:pkg.func\u2C51\n" " int: 1\n"); test ("uprobe:/with#hash:asdf { 1 }", "Program\n" " uprobe:/with#hash:asdf\n" " int: 1\n"); test("uprobe:/my/program:1234 { 1; }", "Program\n" " uprobe:/my/program:1234\n" " int: 1\n"); // Trailing alnum chars are allowed (turns the entire arg into a symbol name) test("uprobe:/my/program:1234abc { 1; }", "Program\n" " uprobe:/my/program:1234abc\n" " int: 1\n"); // Test `:`s in quoted string test("uprobe:/my/program:\"A::f\" { 1; }", "Program\n" " uprobe:/my/program:A::f\n" " int: 1\n"); test_parse_failure("uprobe:f { 1 }"); test_parse_failure("uprobe { 1 }"); } TEST(Parser, usdt) { test("usdt:/my/program:probe { 1; }", "Program\n" " usdt:/my/program:probe\n" " int: 1\n"); // Without the escapes needed for C++ to compile: // usdt:/my/program:"\"probe\"" { 1; } // test("usdt:/my/program:\"\\\"probe\\\"\" { 1; }", "Program\n" " usdt:/my/program:\"probe\"\n" " int: 1\n"); test_parse_failure("usdt { 1 }"); } TEST(Parser, usdt_namespaced_probe) { test("usdt:/my/program:namespace:probe { 1; }", "Program\n" " usdt:/my/program:namespace:probe\n" " int: 1\n"); test("usdt:/my/program*:namespace:probe { 1; }", "Program\n" " usdt:/my/program*:namespace:probe\n" " int: 1\n"); test("usdt:/my/*program:namespace:probe { 1; }", "Program\n" " usdt:/my/*program:namespace:probe\n" " int: 1\n"); test("usdt:*my/program*:namespace:probe { 1; }", "Program\n" " usdt:*my/program*:namespace:probe\n" " int: 1\n"); } TEST(Parser, escape_chars) { test("kprobe:sys_open { \"newline\\nand tab\\tcr\\rbackslash\\\\quote\\\"here oct\\1009hex\\x309\" }", "Program\n" " kprobe:sys_open\n" " string: newline\\nand tab\\tcr\\rbackslash\\\\quote\\\"here oct@9hex09\n"); } TEST(Parser, begin_probe) { test("BEGIN { 1 }", "Program\n" " BEGIN\n" " int: 1\n"); test_parse_failure("BEGIN:f { 1 }"); test_parse_failure("BEGIN:path:f { 1 }"); } TEST(Parser, end_probe) { test("END { 1 }", "Program\n" " END\n" " int: 1\n"); test_parse_failure("END:f { 1 }"); test_parse_failure("END:path:f { 1 }"); } TEST(Parser, tracepoint_probe) { test("tracepoint:sched:sched_switch { 1 }", "Program\n" " tracepoint:sched:sched_switch\n" " int: 1\n"); test("tracepoint:* { 1 }", "Program\n" " tracepoint:*:*\n" " int: 1\n"); test_parse_failure("tracepoint:f { 1 }"); test_parse_failure("tracepoint { 1 }"); } TEST(Parser, profile_probe) { test("profile:ms:997 { 1 }", "Program\n" " profile:ms:997\n" " int: 1\n"); test_parse_failure("profile:ms:nan { 1 }"); test_parse_failure("profile:f { 1 }"); test_parse_failure("profile { 1 }"); test_parse_failure("profile:s:1b { 1 }"); } TEST(Parser, interval_probe) { test("interval:s:1 { 1 }", "Program\n" " interval:s:1\n" " int: 1\n"); test("interval:s:1e3 { 1 }", "Program\n" " interval:s:1000\n" " int: 1\n"); test("interval:s:1_0_0_0 { 1 }", "Program\n" " interval:s:1000\n" " int: 1\n"); test_parse_failure("interval:s:1b { 1 }"); } TEST(Parser, software_probe) { test("software:faults:1000 { 1 }", "Program\n" " software:faults:1000\n" " int: 1\n"); test("software:faults:1e3 { 1 }", "Program\n" " software:faults:1000\n" " int: 1\n"); test("software:faults:1_000 { 1 }", "Program\n" " software:faults:1000\n" " int: 1\n"); test_parse_failure("software:faults:1b { 1 }"); } TEST(Parser, hardware_probe) { test("hardware:cache-references:1000000 { 1 }", "Program\n" " hardware:cache-references:1000000\n" " int: 1\n"); test("hardware:cache-references:1e6 { 1 }", "Program\n" " hardware:cache-references:1000000\n" " int: 1\n"); test("hardware:cache-references:1_000_000 { 1 }", "Program\n" " hardware:cache-references:1000000\n" " int: 1\n"); test_parse_failure("hardware:cache-references:1b { 1 }"); } TEST(Parser, watchpoint_probe) { test("watchpoint:1234:8:w { 1 }", "Program\n" " watchpoint:1234:8:w\n" " int: 1\n"); test_parse_failure("watchpoint:1b:8:w { 1 }"); test_parse_failure("watchpoint:1:8a:w { 1 }"); test_parse_failure("watchpoint:1b:8a:w { 1 }"); test_parse_failure("watchpoint:+arg0:8:rw { 1 }"); test_parse_failure("watchpoint:func1:8:rw { 1 }"); } TEST(Parser, asyncwatchpoint_probe) { test("asyncwatchpoint:1234:8:w { 1 }", "Program\n" " asyncwatchpoint:1234:8:w\n" " int: 1\n"); test_parse_failure("asyncwatchpoint:1b:8:w { 1 }"); test_parse_failure("asyncwatchpoint:1:8a:w { 1 }"); test_parse_failure("asyncwatchpoint:1b:8a:w { 1 }"); test_parse_failure("asyncwatchpoint:+arg0:8:rw { 1 }"); test_parse_failure("asyncwatchpoint:func1:8:rw { 1 }"); } TEST(Parser, multiple_attach_points_kprobe) { test("BEGIN,kprobe:sys_open,uprobe:/bin/sh:foo,tracepoint:syscalls:sys_enter_* { 1 }", "Program\n" " BEGIN\n" " kprobe:sys_open\n" " uprobe:/bin/sh:foo\n" " tracepoint:syscalls:sys_enter_*\n" " int: 1\n"); } TEST(Parser, character_class_attach_point) { test("kprobe:[Ss]y[Ss]_read { 1 }", "Program\n" " kprobe:[Ss]y[Ss]_read\n" " int: 1\n"); } TEST(Parser, wildcard_probetype) { test("t*point:sched:sched_switch { 1; }", "Program\n" " tracepoint:sched:sched_switch\n" " int: 1\n"); test("*ware:* { 1; }", "Program\n" " hardware:*\n" " software:*\n" " int: 1\n"); test("*:/bin/sh:* { 1; }", "Program\n" " uprobe:/bin/sh:*\n" " usdt:/bin/sh:*\n" " int: 1\n"); } TEST(Parser, wildcard_attach_points) { test("kprobe:sys_* { 1 }", "Program\n" " kprobe:sys_*\n" " int: 1\n"); test("kprobe:*blah { 1 }", "Program\n" " kprobe:*blah\n" " int: 1\n"); test("kprobe:sys*blah { 1 }", "Program\n" " kprobe:sys*blah\n" " int: 1\n"); test("kprobe:* { 1 }", "Program\n" " kprobe:*\n" " int: 1\n"); test("kprobe:sys_* { @x = cpu*retval }", "Program\n" " kprobe:sys_*\n" " =\n" " map: @x\n" " *\n" " builtin: cpu\n" " builtin: retval\n"); test("kprobe:sys_* { @x = *arg0 }", "Program\n" " kprobe:sys_*\n" " =\n" " map: @x\n" " dereference\n" " builtin: arg0\n"); } TEST(Parser, wildcard_path) { test("uprobe:/my/program*:* { 1; }", "Program\n" " uprobe:/my/program*:*\n" " int: 1\n"); test("uprobe:/my/program*:func { 1; }", "Program\n" " uprobe:/my/program*:func\n" " int: 1\n"); test("uprobe:*my/program*:func { 1; }", "Program\n" " uprobe:*my/program*:func\n" " int: 1\n"); test("uprobe:/my/program*foo:func { 1; }", "Program\n" " uprobe:/my/program*foo:func\n" " int: 1\n"); test("usdt:/my/program*:* { 1; }", "Program\n" " usdt:/my/program*:*\n" " int: 1\n"); test("usdt:/my/program*:func { 1; }", "Program\n" " usdt:/my/program*:func\n" " int: 1\n"); test("usdt:*my/program*:func { 1; }", "Program\n" " usdt:*my/program*:func\n" " int: 1\n"); test("usdt:/my/program*foo:func { 1; }", "Program\n" " usdt:/my/program*foo:func\n" " int: 1\n"); // Make sure calls or builtins don't cause issues test("usdt:/my/program*avg:func { 1; }", "Program\n" " usdt:/my/program*avg:func\n" " int: 1\n"); test("usdt:/my/program*nsecs:func { 1; }", "Program\n" " usdt:/my/program*nsecs:func\n" " int: 1\n"); } TEST(Parser, dot_in_func) { test("uprobe:/my/go/program:runtime.main.func1 { 1; }", "Program\n" " uprobe:/my/go/program:runtime.main.func1\n" " int: 1\n"); } TEST(Parser, wildcard_func) { test("usdt:/my/program:abc*cd { 1; }", "Program\n" " usdt:/my/program:abc*cd\n" " int: 1\n"); test("usdt:/my/program:abc*c*d { 1; }", "Program\n" " usdt:/my/program:abc*c*d\n" " int: 1\n"); std::string keywords[] = { "arg0", "args", "curtask", "func", "gid" "rand", "uid", "avg", "cat", "exit", "kaddr", "min", "printf", "usym", "kstack", "ustack", "bpftrace", "perf", "uprobe", "kprobe", }; for(auto kw : keywords) { test("usdt:/my/program:"+ kw +"*c*d { 1; }", "Program\n" " usdt:/my/program:"+ kw + "*c*d\n" " int: 1\n"); test("usdt:/my/program:abc*"+ kw +"*c*d { 1; }", "Program\n" " usdt:/my/program:abc*"+ kw + "*c*d\n" " int: 1\n"); } } TEST(Parser, short_map_name) { test("kprobe:sys_read { @ = 1 }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @\n" " int: 1\n"); } TEST(Parser, include) { test("#include \nkprobe:sys_read { @x = 1 }", "#include \n" "\n" "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " int: 1\n"); } TEST(Parser, include_quote) { test("#include \"stdio.h\"\nkprobe:sys_read { @x = 1 }", "#include \"stdio.h\"\n" "\n" "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " int: 1\n"); } TEST(Parser, include_multiple) { test("#include \n#include \"blah\"\n#include \nkprobe:sys_read { @x = 1 }", "#include \n" "#include \"blah\"\n" "#include \n" "\n" "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " int: 1\n"); } TEST(Parser, brackets) { test("kprobe:sys_read { (arg0*arg1) }", "Program\n" " kprobe:sys_read\n" " *\n" " builtin: arg0\n" " builtin: arg1\n"); } TEST(Parser, cast) { test("kprobe:sys_read { (struct mytype)arg0; }", "Program\n" " kprobe:sys_read\n" " (struct mytype)\n" " builtin: arg0\n"); test("kprobe:sys_read { (union mytype)arg0; }", "Program\n" " kprobe:sys_read\n" " (union mytype)\n" " builtin: arg0\n"); } TEST(Parser, cast_ptr) { test("kprobe:sys_read { (struct mytype*)arg0; }", "Program\n" " kprobe:sys_read\n" " (struct mytype*)\n" " builtin: arg0\n"); test("kprobe:sys_read { (union mytype*)arg0; }", "Program\n" " kprobe:sys_read\n" " (union mytype*)\n" " builtin: arg0\n"); } TEST(Parser, cast_typedef) { test("kprobe:sys_read { (mytype)arg0; }", "Program\n" " kprobe:sys_read\n" " (mytype)\n" " builtin: arg0\n"); } TEST(Parser, cast_ptr_typedef) { test("kprobe:sys_read { (mytype*)arg0; }", "Program\n" " kprobe:sys_read\n" " (mytype*)\n" " builtin: arg0\n"); } TEST(Parser, cast_or_expr1) { test("kprobe:sys_read { (mytype)*arg0; }", "Program\n" " kprobe:sys_read\n" " (mytype)\n" " dereference\n" " builtin: arg0\n"); } TEST(Parser, cast_or_expr2) { test("kprobe:sys_read { (arg1)*arg0; }", "Program\n" " kprobe:sys_read\n" " *\n" " builtin: arg1\n" " builtin: arg0\n"); } TEST(Parser, cast_precedence) { test("kprobe:sys_read { (mytype)arg0.field; }", "Program\n" " kprobe:sys_read\n" " (mytype)\n" " .\n" " builtin: arg0\n" " field\n"); test("kprobe:sys_read { (mytype*)arg0->field; }", "Program\n" " kprobe:sys_read\n" " (mytype*)\n" " .\n" " dereference\n" " builtin: arg0\n" " field\n"); test("kprobe:sys_read { (mytype)arg0+123; }", "Program\n" " kprobe:sys_read\n" " +\n" " (mytype)\n" " builtin: arg0\n" " int: 123\n"); } TEST(Parser, dereference_precedence) { test("kprobe:sys_read { *@x+1 }", "Program\n" " kprobe:sys_read\n" " +\n" " dereference\n" " map: @x\n" " int: 1\n"); test("kprobe:sys_read { *@x**@y }", "Program\n" " kprobe:sys_read\n" " *\n" " dereference\n" " map: @x\n" " dereference\n" " map: @y\n"); test("kprobe:sys_read { *@x*@y }", "Program\n" " kprobe:sys_read\n" " *\n" " dereference\n" " map: @x\n" " map: @y\n"); test("kprobe:sys_read { *@x.myfield }", "Program\n" " kprobe:sys_read\n" " dereference\n" " .\n" " map: @x\n" " myfield\n"); } TEST(Parser, field_access) { test("kprobe:sys_read { @x.myfield; }", "Program\n" " kprobe:sys_read\n" " .\n" " map: @x\n" " myfield\n"); test("kprobe:sys_read { @x->myfield; }", "Program\n" " kprobe:sys_read\n" " .\n" " dereference\n" " map: @x\n" " myfield\n"); } TEST(Parser, field_access_builtin) { test("kprobe:sys_read { @x.count; }", "Program\n" " kprobe:sys_read\n" " .\n" " map: @x\n" " count\n"); test("kprobe:sys_read { @x->count; }", "Program\n" " kprobe:sys_read\n" " .\n" " dereference\n" " map: @x\n" " count\n"); } TEST(Parser, array_access) { test("kprobe:sys_read { x[index]; }", "Program\n" " kprobe:sys_read\n" " []\n" " identifier: x\n" " identifier: index\n"); test("kprobe:sys_read { $val = x[index]; }", "Program\n" " kprobe:sys_read\n" " =\n" " variable: $val\n" " []\n" " identifier: x\n" " identifier: index\n"); } TEST(Parser, cstruct) { test("struct Foo { int x, y; char *str; } kprobe:sys_read { 1; }", "struct Foo { int x, y; char *str; };\n" "\n" "Program\n" " kprobe:sys_read\n" " int: 1\n"); } TEST(Parser, cstruct_nested) { test("struct Foo { struct { int x; } bar; } kprobe:sys_read { 1; }", "struct Foo { struct { int x; } bar; };\n" "\n" "Program\n" " kprobe:sys_read\n" " int: 1\n"); } TEST(Parser, unexpected_symbol) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("i:s:1 { < }"), 1); std::string expected = R"(stdin:1:9-10: ERROR: syntax error, unexpected <, expecting } i:s:1 { < } ~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, string_with_tab) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("i:s:1\t\t\t$a"), 1); std::string expected = R"(stdin:1:9-11: ERROR: syntax error, unexpected variable, expecting { i:s:1 $a ~~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, unterminated_string) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("kprobe:f { \"asdf }"), 1); std::string expected = R"(stdin:1:12-19: ERROR: unterminated string kprobe:f { "asdf } ~~~~~~~ stdin:1:12-19: ERROR: syntax error, unexpected end of file, expecting } kprobe:f { "asdf } ~~~~~~~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, kprobe_offset) { test("k:fn+1 {}", "Program\n" " kprobe:fn+1\n"); test("k:fn+0x10 {}", "Program\n" " kprobe:fn+16\n"); test("k:\"fn.abc\"+1 {}", "Program\n" " kprobe:fn.abc+1\n"); test("k:\"fn.abc\"+0x10 {}", "Program\n" " kprobe:fn.abc+16\n"); test_parse_failure("k:asdf+123abc"); } TEST(Parser, kretprobe_offset) { // Not supported yet test_parse_failure("kr:fn+1 { 1 }"); } TEST(Parser, uprobe_offset) { test("u:./test:fn+1 {}", "Program\n" " uprobe:./test:fn+1\n"); test("u:./test:fn+0x10 {}", "Program\n" " uprobe:./test:fn+16\n"); test("u:./test:\"fn.abc\"+1 {}", "Program\n" " uprobe:./test:fn.abc+1\n"); test("u:./test:\"fn.abc\"+0x10 {}", "Program\n" " uprobe:./test:fn.abc+16\n"); } TEST(Parser, uretprobe_offset) { // Not supported yet test_parse_failure("ur:./test:fn+1 { 1 }"); test_parse_failure("uretprobe:/bin/sh:f+0x10 { 1 }"); } TEST(Parser, invalid_increment_decrement) { test_parse_failure("i:s:1 { @=5++}"); test_parse_failure("i:s:1 { @=++5}"); test_parse_failure("i:s:1 { @=5--}"); test_parse_failure("i:s:1 { @=--5}"); test_parse_failure("i:s:1 { @=\"a\"++}"); } TEST(Parser, long_param_overflow) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_NO_THROW( driver.parse_str("i:s:100 { @=$111111111111111111111111111 }")); std::string expected = "stdin:1:11-41: ERROR: param " "$111111111111111111111111111 is out of " "integer range [1, " + std::to_string(std::numeric_limits::max()) + "]\n" + "i:s:100 { @=$111111111111111111111111111 }\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; EXPECT_EQ(out.str(), expected); } TEST(Parser, empty_arguments) { test_parse_failure("::k::vfs_open:: { 1 }"); test_parse_failure("k:vfs_open: { 1 }"); test_parse_failure(":w:0x10000000:8:rw { 1 }"); } TEST(Parser, int_notation) { test("k:f { print(1e6); }", "Program\n kprobe:f\n call: print\n int: 1000000\n"); test("k:f { print(5e9); }", "Program\n kprobe:f\n call: print\n int: 5000000000\n"); test("k:f { print(1e1_0); }", "Program\n kprobe:f\n call: print\n int: 10000000000\n"); test("k:f { print(1_000_000_000_0); }", "Program\n kprobe:f\n call: print\n int: 10000000000\n"); test("k:f { print(1_0_0_0_00_0_000_0); }", "Program\n kprobe:f\n call: print\n int: 10000000000\n"); test("k:f { print(123_456_789_0); }", "Program\n kprobe:f\n call: print\n int: 1234567890\n"); test("k:f { print(0xe5); }", "Program\n kprobe:f\n call: print\n int: 229\n"); test("k:f { print(0x5e5); }", "Program\n kprobe:f\n call: print\n int: 1509\n"); test("k:f { print(0xeeee); }", "Program\n kprobe:f\n call: print\n int: 61166\n"); test("k:f { print(0777); }", "Program\n kprobe:f\n call: print\n int: 511\n"); test("k:f { print(0123); }", "Program\n kprobe:f\n call: print\n int: 83\n"); test("k:f { print(1_000u); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000ul); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000ull); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000l); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000ll); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test_parse_failure("k:f { print(5e-9); }"); test_parse_failure("k:f { print(1e17); }"); test_parse_failure("k:f { print(12e4); }"); test_parse_failure("k:f { print(1_1e100); }"); test_parse_failure("k:f { print(1e1_1_); }"); test_parse_failure("k:f { print(1_1_e100); }"); test_parse_failure("k:f { print(1_1_); }"); test_parse_failure("k:f { print(1ulll); }"); test_parse_failure("k:f { print(1lul); }"); } TEST(Parser, while_loop) { test("i:ms:100 { $a = 0; while($a < 10) { $a++ }}", R"PROG(Program interval:ms:100 = variable: $a int: 0 while( < variable: $a int: 10 ) variable: $a ++ )PROG"); } TEST(Parser, tuple_assignment_error_message) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("i:s:1 { @x = (1, 2); $x.1 = 1; }"), 1); std::string expected = R"(stdin:1:22-30: ERROR: Tuples are immutable once created. Consider creating a new tuple and assigning it instead. i:s:1 { @x = (1, 2); $x.1 = 1; } ~~~~~~~~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, tuple_assignment_error) { test_parse_failure("i:s:1 { (1, 0) = 0 }"); test_parse_failure("i:s:1 { ((1, 0), 3).0.0 = 3 }"); test_parse_failure("i:s:1 { ((1, 0), 3).0 = (0, 1) }"); test_parse_failure("i:s:1 { (1, \"two\", (3, 4)).5 = \"six\"; }"); test_parse_failure("i:s:1 { $a = 1; $a.2 = 3 }"); test_parse_failure("i:s:1 { 0.1 = 1.0 }"); } TEST(Parser, abs_knl_address) { char in_cstr[64]; char out_cstr[64]; snprintf(in_cstr, sizeof(in_cstr), "watchpoint:0x%lx:4:w { 1; }", ULONG_MAX); snprintf(out_cstr, sizeof(out_cstr), "Program\n" " watchpoint:%lu:4:w\n" " int: 1\n", ULONG_MAX); test(std::string(in_cstr), std::string(out_cstr)); } } // namespace parser } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/portability_analyser.cpp000066400000000000000000000057721413460502400215020ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "ast/passes/field_analyser.h" #include "ast/passes/portability_analyser.h" #include "ast/passes/semantic_analyser.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" namespace bpftrace { namespace test { namespace portability_analyser { using ::testing::_; void test(BPFtrace &bpftrace, const std::string &input, int expected_result = 0) { Driver driver(bpftrace); std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.root_, bpftrace, out); ASSERT_EQ(fields.analyse(), 0) << msg.str() << out.str(); ClangParser clang; ASSERT_TRUE(clang.parse(driver.root_, bpftrace)); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); ast::SemanticAnalyser semantics(driver.root_, bpftrace, out, false); ASSERT_EQ(semantics.analyse(), 0) << msg.str() << out.str(); ast::PortabilityAnalyser portability(driver.root_, out); EXPECT_EQ(portability.analyse(), expected_result) << msg.str() << out.str(); } void test(const std::string &input, int expected_result = 0) { auto bpftrace = get_mock_bpftrace(); bpftrace->feature_ = std::make_unique(true); test(*bpftrace, input, expected_result); } TEST(portability_analyser, generic_field_access_disabled) { test("struct Foo { int x;} BEGIN { $f = (struct Foo *)0; $f->x; }", 1); } TEST(portability_analyser, tracepoint_field_access) { test("tracepoint:sched:sched_one { args }", 0); test("tracepoint:sched:sched_one { args->common_field }", 0); test("tracepoint:sched:sched_* { args->common_field }", 0); } #if defined(HAVE_LIBBPF_BTF_DUMP) && defined(HAVE_BCC_KFUNC) #include "btf_common.h" class portability_analyser_btf : public test_btf { }; TEST_F(portability_analyser_btf, kfunc_field_access) { test("kfunc:func_1 { $x = args->a; $y = args->foo1; $z = args->foo2->f.a; }", 0); test("kfunc:func_2 { args->foo1 }", 0); test("kfunc:func_2, kfunc:func_3 { $x = args->foo1; }", 0); } #endif TEST(portability_analyser, positional_params_disabled) { auto bpftrace = get_mock_bpftrace(); bpftrace->add_param("123"); bpftrace->add_param("hello"); test(*bpftrace, "BEGIN { $1 }", 1); test(*bpftrace, "BEGIN { str($2) }", 1); } TEST(portability_analyser, curtask_disabled) { test("BEGIN { curtask }", 1); test("struct task_struct { char comm[16]; } BEGIN { curtask->comm }", 1); } TEST(portability_analyser, selective_probes_disabled) { test("usdt:/bin/sh:probe { 1 }", 1); test("usdt:/bin/sh:namespace:probe { 1 }", 1); auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "watchpoint:0x10000000:8:rw { 1 }", 1); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "watchpoint:0x10000000:8:rw { 1 }", 1); test(*bpftrace, "watchpoint:increment+arg1:4:w { 1 }", 1); test(*bpftrace, "asyncwatchpoint:increment+arg1:4:w { 1 }", 1); } } // namespace portability_analyser } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/probe.cpp000066400000000000000000000052341413460502400163420ustar00rootroot00000000000000#include "ast/bpforc/bpforc.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/field_analyser.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "fake_map.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace bpftrace { namespace test { namespace probe { using bpftrace::ast::AttachPoint; using bpftrace::ast::AttachPointList; using bpftrace::ast::Probe; void gen_bytecode(const std::string &input, std::stringstream &out) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.root_, *bpftrace); EXPECT_EQ(fields.analyse(), 0); ClangParser clang; clang.parse(driver.root_, *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.root_); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; ast::CodegenLLVM codegen(driver.root_, *bpftrace); codegen.generate_ir(); codegen.DumpIR(out); } void compare_bytecode(const std::string &input1, const std::string &input2) { std::stringstream expected_output1; std::stringstream expected_output2; gen_bytecode(input1, expected_output1); gen_bytecode(input2, expected_output2); EXPECT_EQ(expected_output1.str(), expected_output2.str()); } TEST(probe, short_name) { compare_bytecode("tracepoint:a:b { args }", "t:a:b { args }"); compare_bytecode("kprobe:f { pid }", "k:f { pid }"); compare_bytecode("kretprobe:f { pid }", "kr:f { pid }"); compare_bytecode("uprobe:sh:f { 1 }", "u:sh:f { 1 }"); compare_bytecode("profile:hz:997 { 1 }", "p:hz:997 { 1 }"); compare_bytecode("hardware:cache-references:1000000 { 1 }", "h:cache-references:1000000 { 1 }"); compare_bytecode("software:faults:1000 { 1 }", "s:faults:1000 { 1 }"); compare_bytecode("interval:s:1 { 1 }", "i:s:1 { 1 }"); } #ifdef HAVE_LIBBPF_BTF_DUMP #ifdef HAVE_BCC_KFUNC #include "btf_common.h" class probe_btf : public test_btf { }; TEST_F(probe_btf, short_name) { compare_bytecode("kfunc:func_1 { 1 }", "f:func_1 { 1 }"); compare_bytecode("kretfunc:func_1 { 1 }", "fr:func_1 { 1 }"); compare_bytecode("iter:task { 1 }", "it:task { 1 }"); compare_bytecode("iter:task_file { 1 }", "it:task_file { 1 }"); } #endif // HAVE_BCC_KFUNC #endif // HAVE_LIBBPF_BTF_DUMP } // namespace probe } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/procmon.cpp000066400000000000000000000017201413460502400167040ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include #include "child.h" #include "procmon.h" #include "childhelper.h" namespace bpftrace { namespace test { namespace procmon { using ::testing::HasSubstr; TEST(procmon, no_such_proc) { try { ProcMon(1 << 21); FAIL(); } catch (const std::runtime_error &e) { EXPECT_THAT(e.what(), HasSubstr("No such process")); } } TEST(procmon, child_terminates) { auto child = getChild("/bin/ls"); auto procmon = std::make_unique(child->pid()); EXPECT_TRUE(procmon->is_alive()); child->run(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_FALSE(procmon->is_alive()); EXPECT_FALSE(procmon->is_alive()); } TEST(procmon, pid_string) { auto child = getChild("/bin/ls"); auto procmon = std::make_unique(std::to_string(child->pid())); EXPECT_TRUE(procmon->is_alive()); } } // namespace procmon } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/required_resources.cpp000066400000000000000000000125661413460502400211530ustar00rootroot00000000000000#include "required_resources.h" #include #include #include #include "struct.h" #include "types.h" namespace bpftrace { namespace test { // ======================================================================== // It's a bit overkill to completely test every field in `RequiredResources` // so for these tests we opt to get coverage on every type we serialize. // ======================================================================== TEST(required_resources, round_trip_simple) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.probe_ids.emplace_back("itsastring"); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.probe_ids.size(), 1ul); EXPECT_EQ(r.probe_ids[0], "itsastring"); } } TEST(required_resources, round_trip_field_sized_type) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.system_args.emplace_back("field0", std::vector{ Field{ .name = "myfield", .type = CreateInt32(), .offset = 123, .is_bitfield = false, .bitfield = Bitfield{ .read_bytes = 1, .access_rshift = 2, .mask = 0xFF, }, } }); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.system_args.size(), 1ul); EXPECT_EQ(std::get<0>(r.system_args[0]), "field0"); auto &fields = std::get<1>(r.system_args[0]); ASSERT_EQ(fields.size(), 1ul); auto &field = fields[0]; EXPECT_EQ(field.name, "myfield"); EXPECT_TRUE(field.type.IsIntTy()); EXPECT_EQ(field.type.GetSize(), 4ul); EXPECT_EQ(field.offset, 123); EXPECT_EQ(field.is_bitfield, false); EXPECT_EQ(field.bitfield.read_bytes, 1ul); EXPECT_EQ(field.bitfield.access_rshift, 2ul); EXPECT_EQ(field.bitfield.mask, 0xFFul); } } TEST(required_resources, round_trip_map_sized_type) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.map_vals.insert({ "mymap", CreateInet(3) }); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.map_vals.count("mymap"), 1ul); auto &type = r.map_vals["mymap"]; EXPECT_TRUE(type.IsInetTy()); EXPECT_EQ(type.GetSize(), 3ul); } } TEST(required_resources, round_trip_map_lhist_args) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.lhist_args.insert({ "mymap", LinearHistogramArgs{ .min = 99, .max = 123, .step = 33, } }); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.lhist_args.count("mymap"), 1ul); auto &args = r.lhist_args["mymap"]; EXPECT_EQ(args.min, 99); EXPECT_EQ(args.max, 123); EXPECT_EQ(args.step, 33); } } TEST(required_resources, round_trip_set_stack_type) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.stackid_maps.insert(StackType{ .limit = 33, .mode = StackMode::perf, }); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.stackid_maps.size(), 1ul); for (const auto &st : r.stackid_maps) { EXPECT_EQ(st.limit, 33ul); EXPECT_EQ(st.mode, StackMode::perf); } } } TEST(required_resources, round_trip_probes) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; Probe p; p.type = ProbeType::hardware; p.path = "mypath"; p.index = 3; r.special_probes.emplace_back(std::move(p)); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.special_probes.size(), 1ul); auto &probe = r.special_probes[0]; EXPECT_EQ(probe.type, ProbeType::hardware); EXPECT_EQ(probe.path, "mypath"); EXPECT_EQ(probe.index, 3); } } TEST(required_resources, round_trip_multiple_members) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.join_args.emplace_back("joinarg0"); r.stackid_maps.insert(StackType{ .limit = 33, .mode = StackMode::perf, }); r.needs_elapsed_map = true; r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.join_args.size(), 1ul); EXPECT_EQ(r.join_args[0], "joinarg0"); ASSERT_EQ(r.stackid_maps.size(), 1ul); for (const auto &st : r.stackid_maps) { EXPECT_EQ(st.limit, 33ul); EXPECT_EQ(st.mode, StackMode::perf); } EXPECT_TRUE(r.needs_elapsed_map); } } } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/runtime-tests.sh000077500000000000000000000006751413460502400177150ustar00rootroot00000000000000#!/bin/bash if [[ $EUID -ne 0 ]]; then >&2 echo "Must be run as root" exit 1 fi set -e; pushd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 BPFTRACE_RUNTIME_TEST_EXECUTABLE=${BPFTRACE_RUNTIME_TEST_EXECUTABLE:-../src/}; export BPFTRACE_RUNTIME_TEST_EXECUTABLE; echo "====================" echo "bpftrace --info:" echo "====================" "${BPFTRACE_RUNTIME_TEST_EXECUTABLE}/bpftrace" --info python3 runtime/engine/main.py $@ bpftrace-0.14.0/tests/runtime/000077500000000000000000000000001413460502400162065ustar00rootroot00000000000000bpftrace-0.14.0/tests/runtime/addrspace000066400000000000000000000003561413460502400200630ustar00rootroot00000000000000NAME openat uptr RUN {{BPFTRACE}} -e 't:syscalls:sys_enter_openat /comm == "syscall"/ { print(str(uptr(args->filename))) }' -c "./testprogs/syscall openat" EXPECT bpftrace_runtime_test_syscall REQUIRES_FEATURE probe_read_kernel TIMEOUT 5 bpftrace-0.14.0/tests/runtime/array000066400000000000000000000135161413460502400172550ustar00rootroot00000000000000NAME array element access - assign to map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; @x = $a->x[0]; exit(); } EXPECT @x: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array element access - assign to var PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; $x = $a->x[0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array element access - out of bounds PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; $x = $a->x[5]; printf("%d\n", $x); exit(); } EXPECT the index 5 is out of bounds for array of size 4 TIMEOUT 5 AFTER ./testprogs/array_access NAME array element access via assignment into var PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = ((struct A *) arg0)->x; $x = $a[0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array element access via assignment into map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a[0] = ((struct A *) arg0)->x; printf("Result: %d\n", @a[0][0]); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array element access via assignment into map and var PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a[0] = ((struct A *) arg0)->x; $x = @a[0]; printf("Result: %d\n", $x[0]); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array assignment into map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a = ((struct A *) arg0)->x; exit(); } EXPECT @a: \[1,2,3,4\] TIMEOUT 5 AFTER ./testprogs/array_access NAME array as a map key PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @x[((struct A *) arg0)->x] = 0; exit(); } EXPECT @x\[\[1,2,3,4\]\]: 0 TIMEOUT 5 AFTER ./testprogs/array_access NAME array as a part of map multikey PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @x[((struct A *) arg0)->x, 42] = 0; exit(); } EXPECT @x\[\[1,2,3,4\], 42\]: 0 TIMEOUT 5 AFTER ./testprogs/array_access NAME array as a map key assigned from another map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a = ((struct A *) arg0)->x; @x[@a] = 0; exit(); } EXPECT @x\[\[1,2,3,4\]\]: 0 TIMEOUT 5 AFTER ./testprogs/array_access NAME array in a tuple PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @x = (1, ((struct A *) arg0)->x); exit(); } EXPECT @x: \(1, \[1,2,3,4\]\) TIMEOUT 5 AFTER ./testprogs/array_access NAME multi-dimensional array element access PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { $x = ((struct B *) arg1)->y[1][0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 7 TIMEOUT 5 AFTER ./testprogs/array_access NAME multi-dimensional array element access via assignment into var PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { $b = ((struct B *) arg1)->y; $y = $b[1][0]; printf("Result: %d\n", $y); exit(); } EXPECT Result: 7 TIMEOUT 5 AFTER ./testprogs/array_access NAME multi-dimensional array element access via assignment into map PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b[0] = ((struct B *) arg1)->y; printf("Result: %d\n", @b[0][1][0]); exit(); } EXPECT Result: 7 TIMEOUT 5 AFTER ./testprogs/array_access NAME multi-dimensional array element access via assignment into map and var PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b[0] = ((struct B *) arg1)->y; $x = @b[0]; printf("Result: %d\n", $x[1][0]); exit(); } EXPECT Result: 7 TIMEOUT 5 AFTER ./testprogs/array_access NAME multi-dimensional array assignment into map PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b = ((struct B *) arg1)->y; exit(); } EXPECT @b: \[\[5,6\],\[7,8\]\] TIMEOUT 5 AFTER ./testprogs/array_access NAME multi-dimensional array as a map key PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @x[((struct B *) arg1)->y] = 0; exit(); } EXPECT @x\[\[\[5,6\],\[7,8\]\]]: 0 TIMEOUT 5 AFTER ./testprogs/array_access NAME multi-dimensional array as a map key assigned from another map PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b = ((struct B *) arg1)->y; @x[@b] = 0; exit(); } EXPECT @x\[\[\[5,6\],\[7,8\]\]]: 0 TIMEOUT 5 AFTER ./testprogs/array_access NAME array as pointer element access - assign to map PROG uprobe:./testprogs/array_access:test_array { $a = (int32 *) arg0; @x = $a[0]; exit(); } EXPECT @x: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array as pointer element access - assign to var PROG uprobe:./testprogs/array_access:test_array { $a = (int32 *) arg0; $x = $a[0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array as pointer access via assignment into var PROG uprobe:./testprogs/array_access:test_array { $a = (int32 *) arg0; $x = $a[0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array as pointer element access via assignment into map PROG uprobe:./testprogs/array_access:test_array { @a[0] = (int32 *) arg0; printf("Result: %d\n", @a[0][0]); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array as pointer element access via assignment into map and var PROG uprobe:./testprogs/array_access:test_array { @a[0] = (int32 *) arg0; $x = @a[0]; printf("Result: %d\n", $x[0]); exit(); } EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access NAME array element access via positional index RUN {{BPFTRACE}} -e 'struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; $x = $a->x[$1]; printf("Result: %d\n", $x); exit(); }' 0 EXPECT Result: 1 TIMEOUT 5 AFTER ./testprogs/array_access bpftrace-0.14.0/tests/runtime/banned_probes000066400000000000000000000014531413460502400207350ustar00rootroot00000000000000NAME kretprobe:_raw_spin_lock is banned PROG kretprobe:_raw_spin_lock { exit(); } EXPECT error: kretprobe:_raw_spin_lock can't be used as it might lock up your system. TIMEOUT 1 NAME kretprobe:queued_spin_lock_slowpath is banned PROG kretprobe:queued_spin_lock_slowpath { exit(); } EXPECT error: kretprobe:queued_spin_lock_slowpath can't be used as it might lock up your system. TIMEOUT 1 NAME kretprobe:_raw_spin_unlock_irqrestore is banned PROG kretprobe:_raw_spin_unlock_irqrestore { exit(); } EXPECT error: kretprobe:_raw_spin_unlock_irqrestore can't be used as it might lock up your system. TIMEOUT 1 NAME kretprobe:_raw_spin_lock_irqsave is banned PROG kretprobe:_raw_spin_lock_irqsave { exit(); } EXPECT error: kretprobe:_raw_spin_lock_irqsave can't be used as it might lock up your system. TIMEOUT 1 bpftrace-0.14.0/tests/runtime/basic000066400000000000000000000115311413460502400172130ustar00rootroot00000000000000NAME it shows version RUN {{BPFTRACE}} --version EXPECT bpftrace v TIMEOUT 1 NAME it shows usage with help flag RUN {{BPFTRACE}} -h EXPECT USAGE TIMEOUT 1 NAME it shows usage with bad flag RUN {{BPFTRACE}} -idonotexist EXPECT USAGE TIMEOUT 1 NAME errors on non existent file RUN {{BPFTRACE}} non_existent_file.bt EXPECT ERROR: failed to open file 'non_existent_file.bt': No such file or directory TIMEOUT 1 NAME piped script RUN {{BPFTRACE}} - < runtime/scripts/hello_world.bt EXPECT hello world! TIMEOUT 1 NAME it lists kprobes RUN {{BPFTRACE}} -l | grep kprobes EXPECT kprobe TIMEOUT 1 NAME it lists tracepoints RUN {{BPFTRACE}} -l | grep tracepoint EXPECT tracepoint TIMEOUT 1 NAME it lists software events RUN {{BPFTRACE}} -l | grep software EXPECT software TIMEOUT 1 NAME it lists hardware events RUN {{BPFTRACE}} -l | grep hardware EXPECT hardware TIMEOUT 1 NAME it lists kfuncs RUN {{BPFTRACE}} -l | grep kfunc EXPECT kfunc REQUIRES_FEATURE btf TIMEOUT 1 NAME it lists kfunc params RUN {{BPFTRACE}} -lv "kfunc:*" | grep kfunc EXPECT \s+[a-zA-Z_\*\s]+ REQUIRES_FEATURE btf TIMEOUT 1 NAME it lists kprobes with regex filter RUN {{BPFTRACE}} -l "kprobe:*" EXPECT kprobe: TIMEOUT 1 NAME it lists kretprobes with regex filter RUN {{BPFTRACE}} -l "kretprobe:*" EXPECT kretprobe: TIMEOUT 1 NAME it lists uprobes with regex filter RUN {{BPFTRACE}} -l "uprobe:./testprogs/syscall:*" EXPECT uprobe: TIMEOUT 1 NAME it lists uretprobes with regex filter RUN {{BPFTRACE}} -l "uretprobe:./testprogs/syscall:*" EXPECT uretprobe: TIMEOUT 1 NAME it lists tracepoints with regex filter RUN {{BPFTRACE}} -l "tracepoint:raw_syscalls:*" EXPECT tracepoint:raw_syscalls:sys_exit TIMEOUT 1 NAME it lists software events with regex filter RUN {{BPFTRACE}} -l "software:*" EXPECT software TIMEOUT 1 NAME it lists hardware events with regex filter RUN {{BPFTRACE}} -l "hardware:*" EXPECT hardware TIMEOUT 1 NAME it lists kfuncs events with regex filter RUN {{BPFTRACE}} -l "kfunc:*" EXPECT kfunc REQUIRES_FEATURE btf TIMEOUT 1 NAME it lists kretfuncs events with regex filter RUN {{BPFTRACE}} -l "kretfunc:*" EXPECT kretfunc REQUIRES_FEATURE btf TIMEOUT 1 NAME listing with wildcarded probe type RUN {{BPFTRACE}} -l "*ware:*" EXPECT hardware: EXPECT software: TIMEOUT 1 NAME errors on invalid character in search expression RUN {{BPFTRACE}} -l '\n' EXPECT ERROR: invalid character TIMEOUT 1 NAME pid fails validation with leading non-number RUN {{BPFTRACE}} -p a1111 EXPECT ERROR: pid 'a1111' is not a valid decimal number TIMEOUT 1 NAME pid fails validation with non-number in between RUN {{BPFTRACE}} -p 111a1 EXPECT ERROR: pid '111a1' is not a valid decimal number TIMEOUT 1 NAME pid fails validation with non-numeric argument RUN {{BPFTRACE}} -p not_a_pid EXPECT ERROR: pid 'not_a_pid' is not a valid decimal number TIMEOUT 1 NAME pid outside of valid pid range RUN {{BPFTRACE}} -p 5000000 EXPECT ERROR: pid '5000000' out of valid pid range \[1,4194304\] TIMEOUT 1 NAME libraries under /usr/include are in the search path RUN {{BPFTRACE}} -e "$(echo "#include "; echo "BEGIN { exit(); }")" 2>&1 EXPECT ^((?!file not found).)*$ TIMEOUT 1 NAME non existent library include fails RUN {{BPFTRACE}} -e "$(echo "#include "; echo "BEGIN { exit(); }")" 2>&1 EXPECT file not found TIMEOUT 1 NAME defines work RUN {{BPFTRACE}} -e "$(echo '#define _UNDERSCORE 314'; echo 'BEGIN { printf("%d\\n", _UNDERSCORE); exit(); }')" EXPECT 314 TIMEOUT 1 NAME clear map PROG BEGIN { @ = 1; @a[1] = 1; clear(@); clear(@a); printf("ok\n"); exit(); } EXPECT ok TIMEOUT 1 NAME clear count-map PROG BEGIN { @ = count(); @a[1] = count(); clear(@); clear(@a); exit(); } EXPECT @: 0 TIMEOUT 1 NAME delete map PROG BEGIN { @ = 1; @a[1] = 1; delete(@); delete(@a[1]); printf("ok\n"); exit(); } EXPECT ok TIMEOUT 1 NAME delete count-map PROG BEGIN { @ = count(); @a[1] = count(); delete(@); delete(@a[1]); exit(); } EXPECT @: 0 TIMEOUT 1 NAME increment/decrement map PROG BEGIN { @x = 10; printf("%d", @x++); printf(" %d", ++@x); printf(" %d", @x--); printf(" %d\n", --@x); delete(@x); exit(); } EXPECT 10 12 12 10 TIMEOUT 1 NAME increment/decrement variable PROG BEGIN { $x = 10; printf("%d", $x++); printf(" %d", ++$x); printf(" %d", $x--); printf(" %d\n", --$x); exit(); } EXPECT 10 12 12 10 TIMEOUT 1 NAME spawn child RUN {{BPFTRACE}} -e 'i:ms:500 { printf("%d\n", cpid); }' -c './testprogs/syscall nanosleep 1e9' EXPECT [0-9]+ TIMEOUT 1 NAME info flag RUN {{BPFTRACE}} --info EXPECT perf_event_array: yes TIMEOUT 1 NAME basic while loop PROG BEGIN { $a = 0; while ($a <= 100) { @=avg($a++) } exit(); } EXPECT @: 50 TIMEOUT 5 REQUIRES_FEATURE loop NAME disable warnings RUN {{BPFTRACE}} --no-warnings -e 'BEGIN { @x = stats(10); print(@x, 2); clear(@x); exit();}' 2>&1| grep -c -E "WARNING|invalid option" EXPECT ^0$ TIMEOUT 1 NAME kaddr fails PROG BEGIN { print(kaddr("asdfzzzzzzz")) } EXPECT Failed to resolve kernel symbol TIMEOUT 1 bpftrace-0.14.0/tests/runtime/btf000066400000000000000000000017271413460502400167130ustar00rootroot00000000000000NAME user_supplied_c_def_using_btf PROG struct foo { struct task_struct t; } BEGIN { exit(); } EXPECT Attaching 1 probe... TIMEOUT 5 REQUIRES_FEATURE btf NAME tracepoint_pointer_type_resolution PROG tracepoint:syscalls:sys_enter_nanosleep { args->rqtp->tv_sec; exit(); } EXPECT Attaching 1 probe... TIMEOUT 5 REQUIRES_FEATURE btf NAME tracepoint_nested_pointer_type_resolution PROG tracepoint:napi:napi_poll { args->napi->dev->name; exit(); } EXPECT Attaching 1 probe... TIMEOUT 5 REQUIRES_FEATURE btf NAME enum_value_reference PROG BEGIN { printf("%d\n", sizeof(BTF_VAR_STATIC)); exit(); } EXPECT ^8$ TIMEOUT 5 REQUIRES_FEATURE btf NAME redefine_btf_type PROG struct task_struct { int x; } BEGIN { printf("%d\n", curtask->x); } EXPECT -?[0-9][0-9]* TIMEOUT 5 REQUIRES_FEATURE btf NAME redefine_btf_type_missing_def PROG struct task_struct { struct thread_info x; } BEGIN { printf("%d\n", curtask->x.status); } EXPECT error:.*'struct thread_info' TIMEOUT 5 REQUIRES_FEATURE btf bpftrace-0.14.0/tests/runtime/builtin000066400000000000000000000124041413460502400176000ustar00rootroot00000000000000NAME pid PROG i:ms:1 { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT SUCCESS pid [0-9][0-9]* TIMEOUT 5 NAME tid PROG i:ms:1 { printf("SUCCESS '$test' %d\n", tid); exit(); } EXPECT SUCCESS tid [0-9][0-9]* TIMEOUT 5 NAME uid PROG i:ms:1 { printf("SUCCESS '$test' %d\n", uid); exit(); } EXPECT SUCCESS uid [0-9][0-9]* TIMEOUT 5 NAME gid PROG i:ms:1 { printf("SUCCESS '$test' %d\n", gid); exit(); } EXPECT SUCCESS gid [0-9][0-9]* TIMEOUT 5 NAME nsecs PROG i:ms:1 { printf("SUCCESS '$test' %d\n", nsecs); exit(); } EXPECT SUCCESS nsecs -?[0-9][0-9]* TIMEOUT 5 NAME elapsed PROG i:ms:1 { printf("SUCCESS '$test' %d\n", elapsed); exit(); } EXPECT SUCCESS elapsed -?[0-9][0-9]* TIMEOUT 5 NAME cpu PROG i:ms:1 { printf("SUCCESS '$test' %d\n", cpu); exit(); } EXPECT SUCCESS cpu -?[0-9][0-9]* TIMEOUT 5 NAME comm PROG k:vfs_read { printf("SUCCESS '$test' %s\n", comm); exit(); } EXPECT SUCCESS comm .* TIMEOUT 5 AFTER ./testprogs/syscall read NAME stack PROG k:vfs_read{ printf("SUCCESS '$test' %s\n", stack); exit(); } EXPECT SUCCESS stack TIMEOUT 5 AFTER ./testprogs/syscall read NAME kstack PROG k:vfs_read{ printf("SUCCESS '$test' %s\n", kstack); exit(); } EXPECT SUCCESS kstack TIMEOUT 5 AFTER ./testprogs/syscall read NAME ustack PROG k:vfs_read { printf("SUCCESS '$test' %s\n", ustack); exit(); } EXPECT SUCCESS ustack TIMEOUT 5 AFTER ./testprogs/syscall read NAME arg PROG k:vfs_read { printf("SUCCESS '$test' %d\n", arg0); exit(); } EXPECT SUCCESS arg -?[0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME sarg PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS '$test' %d %d\n", sarg0, sarg1); exit(); } EXPECT SUCCESS sarg 32 64 ARCH x86_64 TIMEOUT 5 AFTER ./testprogs/stack_args NAME sarg PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS '$test' %d %d\n", sarg0, sarg1); exit(); } EXPECT SUCCESS sarg 128 256 ARCH aarch64|ppc64|ppc64le TIMEOUT 5 AFTER ./testprogs/stack_args NAME sarg PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS '$test' %d %d\n", sarg0, sarg1); exit(); } EXPECT SUCCESS sarg 16 32 ARCH s390x TIMEOUT 5 AFTER ./testprogs/stack_args NAME retval PROG kretprobe:vfs_read { printf("SUCCESS '$test' %d\n", retval); exit(); } EXPECT SUCCESS retval .* TIMEOUT 5 AFTER ./testprogs/syscall read NAME func PROG k:vfs_read { printf("SUCCESS '$test' %s\n", func); exit(); } EXPECT SUCCESS func .* TIMEOUT 5 AFTER ./testprogs/syscall read NAME func_uprobe PROG uprobe:./testprogs/uprobe_negative_retval:function1 { printf("SUCCESS %s\n", func); exit(); } EXPECT SUCCESS .* AFTER ./testprogs/uprobe_negative_retval TIMEOUT 5 NAME username PROG i:ms:1 { printf("SUCCESS '$test' %s\n", username); exit(); } EXPECT SUCCESS username .* TIMEOUT 5 NAME probe PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n", probe); exit(); } EXPECT SUCCESS probe kprobe:do_nanosleep TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME begin probe PROG BEGIN { printf("%s", probe);exit(); } END{printf("-%s\n", probe); } EXPECT ^BEGIN-END$ TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME curtask PROG i:ms:1 { printf("SUCCESS '$test' %d\n", curtask); exit(); } EXPECT SUCCESS curtask -?[0-9][0-9]* TIMEOUT 5 NAME curtask_field PROG struct task_struct {int x;} i:ms:1 { printf("SUCCESS '$test' %d\n", curtask->x); exit(); } EXPECT SUCCESS curtask_field -?[0-9][0-9]* TIMEOUT 5 NAME rand PROG i:ms:1 { printf("SUCCESS '$test' %d\n", rand); exit(); } EXPECT SUCCESS rand -?[0-9][0-9]* TIMEOUT 5 NAME cgroup PROG i:ms:1 { printf("SUCCESS '$test' %d\n", cgroup); exit(); } EXPECT SUCCESS cgroup -?[0-9][0-9]* TIMEOUT 5 MIN_KERNEL 4.18 NAME ctx PROG struct x {unsigned long x}; i:ms:1 { printf("SUCCESS '$test' %d\n", ((struct x*)ctx)->x); exit(); } EXPECT SUCCESS ctx -?[0-9][0-9]* TIMEOUT 5 NAME cat PROG i:ms:1 { cat("/proc/loadavg"); exit(); } EXPECT ^([0-9]+\.[0-9]+ ?)+.*$ TIMEOUT 5 NAME cat limited output ENV BPFTRACE_CAT_BYTES_MAX=1 PROG i:ms:1 { cat("/proc/loadavg"); exit(); } EXPECT ^[0-9]$ TIMEOUT 5 NAME cat format str PROG i:ms:1 { $s = "loadavg"; cat("/proc/%s", $s); exit(); } EXPECT ^([0-9]+\.[0-9]+ ?)+.*$ TIMEOUT 5 NAME log size too small ENV BPFTRACE_LOG_SIZE=1 RUN {{BPFTRACE}} -ve 'BEGIN { if (str($1) == str($2)) { printf("%s\n", str($1)); exit() } }' "hello" "hello" EXPECT Error loading program: BEGIN TIMEOUT 5 NAME increase log size RUN {{BPFTRACE}} -ve 'BEGIN { if (str($1) == str($2)) { printf("%s\n", str($1)); exit() } }' "hello" "hello" EXPECT ^Attaching 1 probe TIMEOUT 5 NAME cat "no such file" PROG i:ms:1 { cat("/does/not/exist/file"); exit(); } EXPECT ^ERROR: failed to open file '/does/not/exist/file': No such file or directory$ TIMEOUT 5 NAME sizeof PROG struct Foo { int x; char c; } BEGIN { $x = 1; printf("%d %d %d %d %d\n", sizeof(struct Foo), sizeof((*(struct Foo*)0).x), sizeof((*(struct Foo*)0).c), sizeof(1 == 1), sizeof($x)); exit(); } EXPECT ^8 4 1 8 8$ TIMEOUT 5 NAME sizeof_ints PROG BEGIN { printf("%d %d %d %d %d %d\n", sizeof(uint8), sizeof(int8), sizeof(uint16), sizeof(int16), sizeof(uint32), sizeof(int32)); exit(); } EXPECT ^1 1 2 2 4 4$ TIMEOUT 5 # printf only takes 7 args NAME sizeof_ints_pt2 PROG BEGIN { printf("%d %d\n", sizeof(uint64), sizeof(int64)); exit(); } EXPECT ^8 8$ TIMEOUT 5 NAME sizeof_btf PROG BEGIN { printf("size=%d\n", sizeof(struct task_struct)); exit(); } EXPECT ^size= TIMEOUT 5 REQUIRES_FEATURE btf bpftrace-0.14.0/tests/runtime/call000066400000000000000000000265211413460502400170520ustar00rootroot00000000000000NAME printf PROG i:ms:1 { printf("hi!\n"); exit();} EXPECT hi! TIMEOUT 5 NAME printf_long_fmt PROG i:ms:1 { printf("hi abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz!\n"); exit();} EXPECT ^hi abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz! TIMEOUT 5 NAME printf_argument PROG i:ms:1 { printf("value: %dms100\n", 100); exit();} EXPECT value: 100ms100 TIMEOUT 5 NAME printf_llu PROG BEGIN { @start = nsecs; } i:s:1 { printf("Elapsed time: %llus\n", (nsecs - @start)/1000000000); clear(@start); exit();} EXPECT Elapsed time: 1s TIMEOUT 5 NAME printf_argument_alignment RUN {{BPFTRACE}} -e 'struct Foo { int a; char b[10]; } uprobe:testprogs/uprobe_test:function2 { $foo = (struct Foo *)arg0; $foo2 = (struct Foo *)arg1; printf("%d %s %d %s\n", $foo->a, $foo->b, $foo2->a, $foo2->b) }' -c ./testprogs/uprobe_test EXPECT 123 hello 456 world TIMEOUT 5 NAME printf_more_arguments PROG BEGIN { printf("%dst: %sa; %dnd: %sb;; %drd: %sc;;; %dth: %sd;;;;\\n", 1, "a", 2, "ab", 3, "abc", 4, "abcd"); exit(); } EXPECT 1st: aa; 2nd: abb;; 3rd: abcc;;; 4th: abcdd;;;; TIMEOUT 5 NAME time PROG i:ms:1 { time("%H:%M:%S\n"); exit();} EXPECT [0-9]*:[0-9]*:[0-9]* TIMEOUT 5 NAME time_short PROG i:ms:1 { time("%H-%M:%S\n"); exit();} EXPECT [0-9]*-[0-9]* TIMEOUT 5 NAME join RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo 'A'"); } kprobe:sys_execve { join(arg1); exit();}' EXPECT A TIMEOUT 5 NAME join_delim RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo 'A'"); } kprobe:sys_execve { join(arg1, ","); exit();}' EXPECT A TIMEOUT 5 NAME str PROG t:syscalls:sys_enter_execve { printf("P: %s\n", str(args->filename)); exit();} AFTER ./testprogs/syscall execve /bin/ls EXPECT P: /*. TIMEOUT 5 NAME buf RUN {{BPFTRACE}} -e 'struct MyStruct { const char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4] }; u:./testprogs/complex_struct:func { $s = (struct MyStruct *)arg0; printf("P: %r-%r-%r-%r-%r-%r\n", buf($s->a, 4), buf($s->b, 4), buf($s->c), buf($s->d), buf($s->e), buf($s->f)); exit(); }' -c ./testprogs/complex_struct EXPECT P: \\x09\\x08\\x07\\x06-\\x05\\x04\\x03\\x02-\\x01\\x02\\x03\\x04-\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00-\\x09\\x00\\x0a\\x00\\x0b\\x00\\x0c\\x00-\\x0d\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0e\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0f\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00 TIMEOUT 5 ARCH x86_64|ppc64le|aarch64 NAME buf RUN {{BPFTRACE}} -e 'struct MyStruct { const char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4] }; u:./testprogs/complex_struct:func { $s = (struct MyStruct *)arg0; printf("P: %r-%r-%r-%r-%r-%r\n", buf($s->a, 4), buf($s->b, 4), buf($s->c), buf($s->d), buf($s->e), buf($s->f)); exit(); }' -c ./testprogs/complex_struct EXPECT P: \\x09\\x08\\x07\\x06-\\x05\\x04\\x03\\x02-\\x01\\x02\\x03\\x04-\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08-\\x00\\x09\\x00\\x0a\\x00\\x0b\\x00\\x0c-\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0e\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0f\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x10 TIMEOUT 5 ARCH s390x|ppc64 NAME buf_map_key PROG i:ms:100 { @[buf("ok_key", 6)] = 1; exit(); } EXPECT @\[ok_key\]: 1 TIMEOUT 5 NAME buf_map_multikey PROG BEGIN { @[buf("ok_key", 8), 1] = hist(1); exit(); } EXPECT @\[ok_key\\x00\\x00\, 1] TIMEOUT 5 NAME buf_hist_map_key PROG BEGIN { @[buf("ok_key", 8)] = hist(1); exit(); } EXPECT @\[ok_key\\x00\\x00\] TIMEOUT 5 NAME buf_map_value PROG i:ms:100 { @ = buf("ok_value", 8); exit(); } EXPECT @: ok_value TIMEOUT 5 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); exit();} EXPECT do_nanosleep TIMEOUT 5 ARCH x86_64 AFTER ./testprogs/syscall nanosleep 1e8 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("pswaddr"))); exit();} EXPECT do_nanosleep TIMEOUT 5 ARCH s390x AFTER ./testprogs/syscall nanosleep 1e8 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("pc"))); exit();} EXPECT do_nanosleep TIMEOUT 5 ARCH aarch64 AFTER ./testprogs/syscall nanosleep 1e8 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("nip"))); exit();} EXPECT do_nanosleep TIMEOUT 5 ARCH ppc64|ppc64le AFTER ./testprogs/syscall nanosleep 1e8 NAME system RUN {{BPFTRACE}} --unsafe -e 'i:ms:100 { system("echo 'ok_system'"); exit();}' EXPECT ok_system TIMEOUT 5 NAME system_more_args RUN {{BPFTRACE}} --unsafe -e 'i:ms:100 { system("echo %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7); exit();}' EXPECT 1 2 3 4 5 6 7 TIMEOUT 5 NAME count PROG i:ms:100 { @ = count(); exit();} EXPECT @:\s[0-9]+ TIMEOUT 5 NAME sum PROG kprobe:vfs_read { @bytes[comm] = sum(arg2); exit();} EXPECT @.*\[.*\]\:\s[0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME avg PROG kprobe:vfs_read { @bytes[comm] = avg(arg2); exit();} EXPECT @.*\[.*\]\:\s[0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME min PROG kprobe:vfs_read { @bytes[comm] = min(arg2); exit();} EXPECT @.*\[.*\]\:\s[0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME max PROG kprobe:vfs_read { @bytes[comm] = max(arg2); exit();} EXPECT @.*\[.*\]\:\s[0-9]* AFTER ./testprogs/syscall read TIMEOUT 5 NAME stats PROG kprobe:vfs_read { @bytes[comm] = stats(arg2); exit();} EXPECT @.*\[.*\]\:\scount\s[0-9]*\,\saverage\s[0-9]*\,\stotal\s[0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME hist PROG kretprobe:vfs_read { @bytes = hist(retval); exit();} EXPECT @bytes: *\n[\[(].* AFTER ./testprogs/syscall read TIMEOUT 5 NAME lhist PROG kretprobe:vfs_read { @bytes = lhist(retval, 0, 10000, 1000); exit()} EXPECT @bytes: *\n[\[(].* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kstack PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n%s\n", kstack(), kstack(1)); exit(); } EXPECT SUCCESS kstack TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME kstack perf mode PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n", kstack(perf, 1)); exit(); } EXPECT SUCCESS kstack TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME ustack PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n%s\n", ustack(), ustack(1)); exit(); } EXPECT SUCCESS ustack TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME cat PROG i:ms:1 { cat("/proc/uptime"); exit();} EXPECT [0-9]*.[0-9]* [0-9]*.[0-9]* TIMEOUT 5 NAME cat_more_args PROG i:ms:1 { cat("/%s%s%s%s/%s%s%s", "p", "r", "o", "c", "u", "p", "time"); exit();} EXPECT [0-9]*.[0-9]* [0-9]*.[0-9]* TIMEOUT 5 NAME uaddr RUN {{BPFTRACE}} -e 'uprobe:testprogs/uprobe_test:function1 { printf("0x%lx -- 0x%lx\n", *uaddr("GLOBAL_A"), *uaddr("GLOBAL_C")); exit(); }' -c ./testprogs/uprobe_test EXPECT 0x55555555 -- 0x33333333 TIMEOUT 5 NAME ntop static ip PROG i:ms:100 { printf("IP: %s, %s, %s, %s\n", ntop(1), ntop(0x0100007f), ntop(0xffff0000), ntop(0x0000ffff)); exit() } EXPECT IP: 1.0.0.0, 127.0.0.1, 0.0.255.255, 255.255.0.0 ARCH x86_64|aarch64|ppc64le TIMEOUT 5 NAME ntop static ip PROG i:ms:100 { printf("IP: %s, %s, %s, %s\n", ntop(0x01000000), ntop(0x7f000001), ntop(0x0000ffff), ntop(0xffff0000)); exit() } EXPECT IP: 1.0.0.0, 127.0.0.1, 0.0.255.255, 255.255.0.0 ARCH s390x|ppc64 TIMEOUT 5 NAME usym PROG i:ms:100 { @=usym(1); @a=@; exit(); } EXPECT ^@a: 0x1$ TIMEOUT 5 NAME print_non_map PROG BEGIN { $x = 5; print($x); exit() } EXPECT 5 TIMEOUT 1 NAME print_non_map_builtin PROG BEGIN { print(comm); exit() } EXPECT bpftrace TIMEOUT 1 NAME print_non_map_tuple PROG BEGIN { $t = (1, 2, "string"); print($t); exit() } EXPECT (1, 2, string) TIMEOUT 1 NAME print_non_map_array PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = ((struct A *) arg0)->x; print($a); exit(); } EXPECT \[1,2,3,4\] TIMEOUT 5 AFTER ./testprogs/array_access NAME print_non_map_multi_dimensional_array PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { $b = ((struct B *) arg1)->y; print($b); exit(); } EXPECT \[\[5,6\],\[7,8\]\] TIMEOUT 5 AFTER ./testprogs/array_access NAME print_non_map_struct PROG struct Foo { int m; int n; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); print($f); exit(); } EXPECT { .m = 2, .n = 3 } TIMEOUT 5 AFTER ./testprogs/simple_struct NAME strftime PROG BEGIN { $ts = strftime("%m/%d/%y", nsecs); printf("%s\n", $ts); exit(); } EXPECT [0-9]{2}\/[0-9]{2}\/[0-9]{2} TIMEOUT 5 NAME strftime_as_map_key PROG BEGIN { @[strftime("%m/%d/%y", nsecs)] = 1; exit();} EXPECT \[[0-9]{2}\/[0-9]{2}\/[0-9]{2}\]: 1 TIMEOUT 5 NAME strftime_as_map_value PROG BEGIN { @[nsecs] = strftime("%m/%d/%y", nsecs); exit();} EXPECT @\[[0-9]*\]: [0-9]{2}\/[0-9]{2}\/[0-9]{2} TIMEOUT 5 # Output two microsecond timestamps, 123000 nsecs apart. Use python to evaluate and verify there's a 123us delta # # Note we add a `1` before the timestamp b/c leading zeros (eg `0123`) is invalid integer in python. NAME strftime_microsecond_extension RUN {{BPFTRACE}} -e 'BEGIN { printf("%s - %s\n", strftime("1%f", 1000123000), strftime("1%f", 0)); exit(); }' | tail -n +2 | xargs -I{} python -c "print({})" EXPECT 123 TIMEOUT 1 # Similar to above test but test that rolling over past 1s works as expected NAME strftime_microsecond_extension_rollover RUN {{BPFTRACE}} -e 'BEGIN { printf("%s - %s\n", strftime("1%f", 1000000123000), strftime("1%f", 0)); exit(); }' | tail -n +2 | xargs -I{} python -c "print({})" EXPECT 123 TIMEOUT 1 NAME print_avg_map_args PROG BEGIN { print("BEGIN"); @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 2, 10); print("END"); clear(@); exit(); } EXPECT BEGIN\n@\[c\]: 3\n@\[d\]: 4\n\nEND TIMEOUT 1 NAME print_avg_map_with_large_top PROG BEGIN { print("BEGIN"); @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 10, 10); print("END"); clear(@); exit(); } EXPECT BEGIN\n@\[a\]: 1\n@\[b\]: 2\n@\[c\]: 3\n@\[d\]: 4\n\nEND TIMEOUT 1 NAME print_hist_with_top_arg PROG BEGIN { print("BEGIN"); @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 2); print("END"); clear(@); exit(); } EXPECT BEGIN\n@\[2\]:(.*\n)+@\[3\]:(.*\n)+END TIMEOUT 1 NAME print_hist_with_large_top_arg PROG BEGIN { print("BEGIN"); @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 10); print("END"); clear(@); exit(); } EXPECT BEGIN\n@\[1\]:(.*\n)+@\[2\]:(.*\n)+@\[3\]:(.*\n)+END TIMEOUT 1 NAME path RUN {{BPFTRACE}} -ve 'kfunc:filp_close { $f = path(args->filp->f_path); if (!strncmp($f, "/tmp/bpftrace_runtime_test_syscall_gen_read_temp", 49)) { printf("OK\n"); exit(); } }' EXPECT OK REQUIRES_FEATURE dpath TIMEOUT 5 AFTER ./testprogs/syscall read NAME macaddr RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); printf("P: %s\n", macaddr($s->mac)); exit(); }' -c ./testprogs/complex_struct EXPECT P: 05:04:03:02:01:02 TIMEOUT 5 NAME macaddr as map key RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); @[macaddr($s->mac)] = 1; exit(); }' -c ./testprogs/complex_struct EXPECT @\[05:04:03:02:01:02\]: 1 TIMEOUT 5 NAME macaddr as map value RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); @[1] = macaddr($s->mac); exit(); }' -c ./testprogs/complex_struct EXPECT \[1\]: 05:04:03:02:01:02 TIMEOUT 5 NAME iter:task PROG iter:task { printf("OK"); } EXPECT OK REQUIRES_FEATURE iter:task TIMEOUT 5 NAME iter:task_file PROG iter:task_file { printf("OK"); } EXPECT OK REQUIRES_FEATURE iter:task_file TIMEOUT 5 bpftrace-0.14.0/tests/runtime/complex_types000066400000000000000000000011041413460502400210200ustar00rootroot00000000000000NAME struct-array RUN {{BPFTRACE}} runtime/scripts/struct_array.bt -c ./testprogs/struct_array EXPECT 100 102 104 106 108 -- 1 3 5 7 9 -- 100 98 96 94 92 TIMEOUT 5 NAME struct-array with variables RUN {{BPFTRACE}} runtime/scripts/struct_array_vars.bt -c ./testprogs/struct_array EXPECT 100 102 104 106 108 -- 1 3 5 7 9 -- 100 98 96 94 92 -- 42 TIMEOUT 5 NAME pointer_to_pointer RUN {{BPFTRACE}} -e 'struct Foo { int a; char b[10]; } uprobe:./testprogs/ptr_to_ptr:function { $pp = (struct Foo **)arg0; printf("%d\n", (*$pp)->a); }' -c ./testprogs/ptr_to_ptr EXPECT 123 TIMEOUT 5 bpftrace-0.14.0/tests/runtime/dwarf000066400000000000000000000015241413460502400172360ustar00rootroot00000000000000NAME list uprobe args - basic type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:main' EXPECT int argc REQUIRES_FEATURE dwarf TIMEOUT 5 NAME list uprobe args - pointer type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:function1' EXPECT int\* n REQUIRES_FEATURE dwarf TIMEOUT 5 NAME list uprobe args - pointer type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:function2' EXPECT struct Foo\* foo1 REQUIRES_FEATURE dwarf TIMEOUT 5 NAME uprobe arg by name - char PROG uprobe:./testprogs/uprobe_test:function1 { printf("c = %c\n", args->c); exit(); } EXPECT c = x REQUIRES_FEATURE dwarf TIMEOUT 5 BEFORE ./testprogs/uprobe_test NAME uprobe arg by name - pointer PROG uprobe:./testprogs/uprobe_test:function1 { printf("n = %d\n", *(args->n)); exit(); } EXPECT n = 13 REQUIRES_FEATURE dwarf TIMEOUT 5 BEFORE ./testprogs/uprobe_test bpftrace-0.14.0/tests/runtime/engine/000077500000000000000000000000001413460502400174535ustar00rootroot00000000000000bpftrace-0.14.0/tests/runtime/engine/cmake_vars.py000066400000000000000000000001021413460502400221310ustar00rootroot00000000000000LIBBCC_BPF_CONTAINS_RUNTIME = bool(@LIBBCC_BPF_CONTAINS_RUNTIME@) bpftrace-0.14.0/tests/runtime/engine/main.py000066400000000000000000000057171413460502400207630ustar00rootroot00000000000000#!/usr/bin/python3 import argparse from datetime import timedelta from fnmatch import fnmatch import time from parser import TestParser, UnknownFieldError, RequiredFieldError from runner import Runner, ok, fail, warn def main(test_filter, run_aot_tests): if not test_filter: test_filter = "*" try: test_suite = sorted(TestParser.read_all(run_aot_tests)) test_suite = [ (n, sorted(t)) for n, t in test_suite ] except (UnknownFieldError, RequiredFieldError) as error: print(fail(str(error))) exit(1) # Apply filter filtered_suites = [] for fname, tests in test_suite: filtered_tests = [t for t in tests if fnmatch("{}.{}".format(fname, t.name), test_filter)] if len(filtered_tests) != 0: filtered_suites.append((fname, filtered_tests)) test_suite = filtered_suites total_tests = 0 for fname, suite_tests in test_suite: total_tests += len(suite_tests) failed_tests = [] print(ok("[==========]") + " Running %d tests from %d test cases.\n" % (total_tests, len(test_suite))) start_time = time.time() skipped_tests = [] for fname, tests in test_suite: print(ok("[----------]") + " %d tests from %s" % (len(tests), fname)) for test in tests: status = Runner.run_test(test) if Runner.skipped(status): skipped_tests.append((fname, test, status)) if Runner.failed(status): failed_tests.append("%s.%s" % (fname, test.name)) # TODO(mmarchini) elapsed time per test suite and per test (like gtest) print(ok("[----------]") + " %d tests from %s\n" % (len(tests), fname)) elapsed = time.time() - start_time total_tests -= len(skipped_tests) # TODO(mmarchini) pretty print time print(ok("[==========]") + " %d tests from %d test cases ran. (%s total)" % (total_tests, len(test_suite), elapsed)) print(ok("[ PASSED ]") + " %d tests." % (total_tests - len(failed_tests))) if skipped_tests: print(warn("[ SKIP ]") + " %d tests, listed below:" % len(skipped_tests)) for test_suite, test, status in skipped_tests: print(warn("[ SKIP ]") + " %s.%s (%s)" % (test_suite, test.name, Runner.skip_reason(test, status))) if failed_tests: print(fail("[ FAILED ]") + " %d tests, listed below:" % len(failed_tests)) for failed_test in failed_tests: print(fail("[ FAILED ]") + " %s" % failed_test) if failed_tests: exit(1) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Runtime tests for bpftrace.') parser.add_argument('--filter', dest='tests_filter', help='filter runtime tests') parser.add_argument('--run-aot-tests', action='store_true', help='Run ahead-of-time compliation tests. Note this would roughly double test time.') args = parser.parse_args() main(args.tests_filter, args.run_aot_tests) bpftrace-0.14.0/tests/runtime/engine/parser.py000066400000000000000000000135511413460502400213260ustar00rootroot00000000000000#!/usr/bin/python3 from collections import namedtuple import os import platform class RequiredFieldError(Exception): pass class UnknownFieldError(Exception): pass class InvalidFieldError(Exception): pass TestStruct = namedtuple( 'TestStruct', [ 'name', 'run', 'prog', 'expect', 'timeout', 'before', 'after', 'suite', 'kernel', 'requirement', 'env', 'arch', 'feature_requirement', 'neg_feature_requirement', ], ) class TestParser(object): @staticmethod def read_all(run_aot_tests): aot_tests = [] for root, subdirs, files in os.walk('./runtime'): for ignore_dir in ["engine", "scripts", "outputs"]: if ignore_dir in subdirs: subdirs.remove(ignore_dir) for filename in files: if filename.startswith("."): continue parser = TestParser.read(root + '/' + filename) if parser[1]: if run_aot_tests: for test in parser[1]: # Only reuse tests that use PROG directive if not test.prog: continue # _replace() creates a new instance w/ specified fields replaced test = test._replace( name='{}.{}'.format(test.suite, test.name), suite='aot') aot_tests.append(test) yield parser if run_aot_tests: yield ('aot', aot_tests) @staticmethod def read(file_name): tests = [] test_lines = [] test_suite = file_name.split('/')[-1] with open (file_name, 'r') as file: lines = file.readlines() line_num = 0 for line in lines: line_num += 1 if line.startswith("#"): continue if line != '\n': test_lines.append(line) if line == '\n' or line_num == len(lines): if test_lines: test_struct = TestParser.__read_test_struct(test_lines, test_suite) if not test_struct.arch or (platform.machine().lower() in test_struct.arch): tests.append(test_struct) test_lines = [] return (test_suite, tests) @staticmethod def __read_test_struct(test, test_suite): name = '' run = '' prog = '' expect = '' timeout = '' before = '' after = '' kernel = '' requirement = '' env = {} arch = [] feature_requirement = set() neg_feature_requirement = set() for item in test: item_split = item.split() item_name = item_split[0] line = ' '.join(item_split[1:]) if item_name == 'NAME': name = line elif item_name == 'RUN': run = line elif item_name == "PROG": prog = line elif item_name == 'EXPECT': expect = line elif item_name == 'TIMEOUT': timeout = int(line.strip(' ')) elif item_name == 'BEFORE': before = line elif item_name == 'AFTER': after = line elif item_name == 'MIN_KERNEL': kernel = line elif item_name == 'REQUIRES': requirement = line elif item_name == 'ENV': for e in line.split(): k, v = e.split('=') env[k]=v elif item_name == 'ARCH': arch = [x.strip() for x in line.split("|")] elif item_name == 'REQUIRES_FEATURE': features = { "loop", "btf", "probe_read_kernel", "dpath", "uprobe_refcount", "signal", "iter:task", "iter:task_file", "libpath_resolv", "dwarf", "aot", } for f in line.split(" "): f = f.strip() if f.startswith("!"): neg_feature_requirement.add(f[1:]) else: feature_requirement.add(f) unknown = (feature_requirement | neg_feature_requirement) - features if len(unknown) > 0: raise UnknownFieldError('%s is invalid for REQUIRES_FEATURE. Suite: %s' % (','.join(unknown), test_suite)) else: raise UnknownFieldError('Field %s is unknown. Suite: %s' % (item_name, test_suite)) if name == '': raise RequiredFieldError('Test NAME is required. Suite: ' + test_suite) elif run == '' and prog == '': raise RequiredFieldError('Test RUN or PROG is required. Suite: ' + test_suite) elif run != '' and prog != '': raise InvalidFieldError('Test RUN and PROG both specified. Suit: ' + test_suite) elif expect == '': raise RequiredFieldError('Test EXPECT is required. Suite: ' + test_suite) elif timeout == '': raise RequiredFieldError('Test TIMEOUT is required. Suite: ' + test_suite) return TestStruct( name, run, prog, expect, timeout, before, after, test_suite, kernel, requirement, env, arch, feature_requirement, neg_feature_requirement) bpftrace-0.14.0/tests/runtime/engine/runner.py000066400000000000000000000243261413460502400213450ustar00rootroot00000000000000#!/usr/bin/python3 import subprocess import signal import os import time from os import environ, uname, devnull from distutils.version import LooseVersion import re from functools import lru_cache import cmake_vars BPF_PATH = environ["BPFTRACE_RUNTIME_TEST_EXECUTABLE"] ENV_PATH = environ["PATH"] ATTACH_TIMEOUT = 5 DEFAULT_TIMEOUT = 5 OK_COLOR = '\033[92m' WARN_COLOR = '\033[94m' ERROR_COLOR = '\033[91m' NO_COLOR = '\033[0m' # TODO(mmarchini) only add colors if terminal supports it def colorify(s, color): return "%s%s%s" % (color, s, NO_COLOR) def ok(s): return colorify(s, OK_COLOR) def warn(s): return colorify(s, WARN_COLOR) def fail(s): return colorify(s, ERROR_COLOR) class TimeoutError(Exception): pass class Runner(object): PASS = 0 FAIL = 1 SKIP_KERNEL_VERSION = 2 TIMEOUT = 3 SKIP_REQUIREMENT_UNSATISFIED = 4 SKIP_ENVIRONMENT_DISABLED = 5 SKIP_FEATURE_REQUIREMENT_UNSATISFIED = 6 SKIP_AOT_NOT_SUPPORTED = 7 @staticmethod def failed(status): return status in [Runner.FAIL, Runner.TIMEOUT] @staticmethod def skipped(status): return status in [ Runner.SKIP_KERNEL_VERSION, Runner.SKIP_REQUIREMENT_UNSATISFIED, Runner.SKIP_ENVIRONMENT_DISABLED, Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED, Runner.SKIP_AOT_NOT_SUPPORTED, ] @staticmethod def skip_reason(test, status): if status == Runner.SKIP_KERNEL_VERSION: return "min Kernel: %s" % test.kernel elif status == Runner.SKIP_REQUIREMENT_UNSATISFIED: return "unmet condition: '%s'" % test.requirement elif status == Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED: neg_reqs = { "!{}".format(f) for f in test.neg_feature_requirement } return "missed feature: '%s'" % ','.join( (neg_reqs | test.feature_requirement)) elif status == Runner.SKIP_ENVIRONMENT_DISABLED: return "disabled by environment variable" elif status == Runner.SKIP_AOT_NOT_SUPPORTED: return "aot does not yet support this" else: raise ValueError("Invalid skip reason: %d" % status) @staticmethod def prepare_bpf_call(test): bpftrace_path = "{}/bpftrace".format(BPF_PATH) bpftrace_aotrt_path = "{}/aot/bpftrace-aotrt".format(BPF_PATH) if test.run: ret = re.sub("{{BPFTRACE}}", bpftrace_path, test.run) ret = re.sub("{{BPFTRACE_AOTRT}}", bpftrace_aotrt_path, ret) return ret else: # PROG # We're only reusing PROG-directive tests for AOT tests if test.suite == 'aot': return "{} -e '{}' --aot /tmp/tmpprog.btaot && {} /tmp/tmpprog.btaot".format( bpftrace_path, test.prog, bpftrace_aotrt_path) else: return "{} -e '{}'".format(bpftrace_path, test.prog) @staticmethod def __handler(signum, frame): raise TimeoutError('TIMEOUT') @staticmethod @lru_cache(maxsize=1) def __get_bpffeature(): cmd = "bpftrace --info" p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env={'PATH': "{}:{}".format(BPF_PATH, ENV_PATH)}, preexec_fn=os.setsid, universal_newlines=True, bufsize=1 ) output = p.communicate()[0] bpffeature = {} bpffeature["loop"] = output.find("Loop support: yes") != -1 bpffeature["probe_read_kernel"] = output.find("probe_read_kernel: yes") != -1 bpffeature["btf"] = output.find("btf (depends on Build:libbpf): yes") != -1 bpffeature["dpath"] = output.find("dpath: yes") != -1 bpffeature["uprobe_refcount"] = \ output.find("uprobe refcount (depends on Build:bcc bpf_attach_uprobe refcount): yes") != -1 bpffeature["signal"] = output.find("send_signal: yes") != -1 bpffeature["iter:task"] = output.find("iter:task: yes") != -1 bpffeature["iter:task_file"] = output.find("iter:task_file: yes") != -1 bpffeature["libpath_resolv"] = output.find("bcc library path resolution: yes") != -1 bpffeature["dwarf"] = output.find("libdw (DWARF support): yes") != -1 bpffeature["aot"] = cmake_vars.LIBBCC_BPF_CONTAINS_RUNTIME return bpffeature @staticmethod def run_test(test): current_kernel = LooseVersion(uname()[2]) if test.kernel and LooseVersion(test.kernel) > current_kernel: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_KERNEL_VERSION full_test_name = test.suite + "." + test.name if full_test_name in os.getenv("RUNTIME_TEST_DISABLE", "").split(","): print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_ENVIRONMENT_DISABLED signal.signal(signal.SIGALRM, Runner.__handler) try: before = None after = None print(ok("[ RUN ] ") + "%s.%s" % (test.suite, test.name)) if test.requirement: with open(devnull, 'w') as dn: if subprocess.call( test.requirement, shell=True, stdout=dn, stderr=dn, env={'PATH': "{}:{}".format(BPF_PATH, ENV_PATH)}, ) != 0: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_REQUIREMENT_UNSATISFIED if test.feature_requirement or test.neg_feature_requirement: bpffeature = Runner.__get_bpffeature() for feature in test.feature_requirement: if feature not in bpffeature: raise ValueError("Invalid feature requirement: %s" % feature) elif not bpffeature[feature]: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED for feature in test.neg_feature_requirement: if feature not in bpffeature: raise ValueError("Invalid feature requirement: %s" % feature) elif bpffeature[feature]: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED if test.before: before = subprocess.Popen(test.before, shell=True, preexec_fn=os.setsid) waited=0 with open(devnull, 'w') as dn: # This might not work for complicated cases, such as if # a test program needs to accept arguments. It covers the # current simple calls with no arguments child_name = os.path.basename(test.before.split()[-1]) while subprocess.call(["pidof", "-s", child_name], stdout=dn, stderr=dn) != 0: time.sleep(0.1) waited+=0.1 if waited > test.timeout: raise TimeoutError('Timed out waiting for BEFORE %s ', test.before) bpf_call = Runner.prepare_bpf_call(test) if test.before: childpid = subprocess.Popen(["pidof", "-s", child_name], stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].strip() bpf_call = re.sub("{{BEFORE_PID}}", str(childpid), bpf_call) env = { 'test': test.name, '__BPFTRACE_NOTIFY_PROBES_ATTACHED': '1', '__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED': '1', } env.update(test.env) p = subprocess.Popen( bpf_call, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, preexec_fn=os.setsid, universal_newlines=True, bufsize=1 ) signal.alarm(ATTACH_TIMEOUT) output = "" while p.poll() is None: nextline = p.stdout.readline() output += nextline if nextline == "__BPFTRACE_NOTIFY_PROBES_ATTACHED\n": signal.alarm(test.timeout or DEFAULT_TIMEOUT) if not after and test.after: after = subprocess.Popen(test.after, shell=True, preexec_fn=os.setsid) break output += p.communicate()[0] signal.alarm(0) result = re.search(test.expect, output, re.M) except (TimeoutError): # Give it a last chance, the test might have worked but the # bpftrace process might still be alive if p.poll() is None: os.killpg(os.getpgid(p.pid), signal.SIGKILL) output += p.communicate()[0] result = re.search(test.expect, output) if not result: print(fail("[ TIMEOUT ] ") + "%s.%s" % (test.suite, test.name)) print('\tCommand: %s' % bpf_call) print('\tTimeout: %s' % test.timeout) print('\tCurrent output: %s' % output) return Runner.TIMEOUT finally: if before and before.poll() is None: os.killpg(os.getpgid(before.pid), signal.SIGKILL) if after and after.poll() is None: os.killpg(os.getpgid(after.pid), signal.SIGKILL) if result: print(ok("[ OK ] ") + "%s.%s" % (test.suite, test.name)) return Runner.PASS elif '__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED' in output: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_AOT_NOT_SUPPORTED else: print(fail("[ FAILED ] ") + "%s.%s" % (test.suite, test.name)) print('\tCommand: ' + bpf_call) print('\tExpected: ' + test.expect) print('\tFound: ' + output.encode("unicode_escape").decode("utf-8")) return Runner.FAIL bpftrace-0.14.0/tests/runtime/file-output000066400000000000000000000017301413460502400204070ustar00rootroot00000000000000NAME printf to file RUN {{BPFTRACE}} -e 'i:ms:10 { printf("%s %d\n", "SUCCESS", 1); exit() }' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT ^SUCCESS 1$ TIMEOUT 5 NAME cat to file RUN {{BPFTRACE}} -e 'i:ms:10 { cat("/proc/loadavg"); exit(); }' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT ^([0-9]+\.[0-9]+ )+.*$ TIMEOUT 5 NAME print map to file RUN {{BPFTRACE}} -e 'i:ms:10 { @=lhist(50, 0, 100, 10); exit();}' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT ^\[50, 60\).*\@+\|$ TIMEOUT 5 NAME system stdout to file RUN {{BPFTRACE}} --unsafe -e 'i:ms:10 { system("cat /proc/loadavg"); exit(); }' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT ^([0-9]+\.[0-9]+ )+.*$ TIMEOUT 5 bpftrace-0.14.0/tests/runtime/intcast000066400000000000000000000011371413460502400176000ustar00rootroot00000000000000NAME Casting retval should work PROG ur:./testprogs/uprobe_negative_retval:main /(int32)retval < 0/ { @[(int32)retval]=count(); exit();} AFTER ./testprogs/uprobe_negative_retval EXPECT ^@\[-100\]: 1$ TIMEOUT 5 NAME Sum casted retval PROG ur:./testprogs/uprobe_negative_retval:function1 { @=stats((int32)retval); exit();} ur:./testprogs/uprobe_negative_retval:main { exit() } AFTER ./testprogs/uprobe_negative_retval EXPECT ^@: count 221, average -10, total -2210$ TIMEOUT 5 NAME Casting ints PROG BEGIN{printf("Values: %d %d\n", (uint8) -10, (uint16) 131087); exit(); } EXPECT Values: 246 15$ TIMEOUT 5 bpftrace-0.14.0/tests/runtime/intptrcast000066400000000000000000000033411413460502400203250ustar00rootroot00000000000000NAME Sum casted value PROG uprobe:./testprogs/intptrcast:fn { @=sum(*(uint16*)(reg("sp")+8)); exit();} EXPECT ^@: 3258 TIMEOUT 5 ARCH x86_64 AFTER ./testprogs/intptrcast NAME Sum casted value PROG uprobe:./testprogs/intptrcast:fn { @=sum(*(uint16*)(reg("sp")+0)); exit();} EXPECT ^@: 3258 TIMEOUT 5 ARCH aarch64 AFTER ./testprogs/intptrcast NAME Sum casted value PROG uprobe:./testprogs/intptrcast:fn { @=sum(*(uint16*)(reg("r1")+96)); exit();} EXPECT ^@: 3258 TIMEOUT 5 ARCH ppc64le AFTER ./testprogs/intptrcast NAME Sum casted value PROG uprobe:./testprogs/intptrcast:fn { @=sum(*(uint16*)(reg("r1")+112)); exit();} EXPECT ^@: 3258 TIMEOUT 5 ARCH ppc64 AFTER ./testprogs/intptrcast NAME Sum casted value PROG uprobe:./testprogs/intptrcast:fn { @=sum(*(uint16*)(reg("r15")+160)); exit();} EXPECT ^@: 4660 TIMEOUT 5 ARCH s390x AFTER ./testprogs/intptrcast NAME Casting ints PROG uprobe:./testprogs/intptrcast:fn { $a=*(uint16*)(reg("sp")+8); printf("%d\n", $a); exit();} EXPECT 3258 TIMEOUT 5 ARCH x86_64 AFTER ./testprogs/intptrcast NAME Casting ints PROG uprobe:./testprogs/intptrcast:fn { $a=*(uint16*)(reg("sp")+0); printf("%d\n", $a); exit();} EXPECT 3258 TIMEOUT 5 ARCH aarch64 AFTER ./testprogs/intptrcast NAME Casting ints PROG uprobe:./testprogs/intptrcast:fn { $a=*(uint16*)(reg("r1")+96); printf("%d\n", $a); exit();} EXPECT 3258 TIMEOUT 5 ARCH ppc64le AFTER ./testprogs/intptrcast NAME Casting ints PROG uprobe:./testprogs/intptrcast:fn { $a=*(uint16*)(reg("r1")+112); printf("%d\n", $a); exit();} EXPECT 3258 TIMEOUT 5 ARCH ppc64 AFTER ./testprogs/intptrcast NAME Casting ints PROG uprobe:./testprogs/intptrcast:fn { $a=*(uint16*)(reg("r15")+160); printf("%d\n", $a); exit();} EXPECT 4660 TIMEOUT 5 ARCH s390x AFTER ./testprogs/intptrcast bpftrace-0.14.0/tests/runtime/json-output000066400000000000000000000152061413460502400204440ustar00rootroot00000000000000NAME invalid_format RUN {{BPFTRACE}} -q -f jsonx -e 'BEGIN { @scalar = 5; exit(); }' EXPECT ^ERROR: Invalid output format "jsonx"$ TIMEOUT 5 NAME scalar RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @scalar = 5; exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/scalar.json")))' EXPECT ^True$ TIMEOUT 5 NAME scalar_str RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @scalar_str = "a b \n d e"; exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/scalar_str.json")))' EXPECT ^True$ TIMEOUT 5 NAME complex RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @complex[comm,2] = 5; exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/complex.json")))' EXPECT ^True$ TIMEOUT 5 NAME map RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @map["key1"] = 2; @map["key2"] = 3; exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/map.json")))' EXPECT ^True$ TIMEOUT 5 NAME histogram RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @hist = hist(2); @hist = hist(1025); exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/hist.json")))' EXPECT ^True$ TIMEOUT 5 NAME multiple histograms RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @["bpftrace"] = hist(2); @["curl"] = hist(-1); @["curl"] = hist(0); @["curl"] = hist(511); @["curl"] = hist(1024); @["curl"] = hist(1025); exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/hist_multiple.json")))' EXPECT ^True$ TIMEOUT 5 NAME linear histogram RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @h = lhist(2, 0, 100, 10); @h = lhist(50, 0, 100, 10); @h = lhist(1000, 0, 100, 10); exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/lhist.json")))' EXPECT ^True$ TIMEOUT 5 NAME multiple linear histograms RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @stats["bpftrace"] = lhist(2, 0, 100, 10); @stats["curl"] = lhist(50, 0, 100, 10); @stats["bpftrace"] = lhist(1000, 0, 100, 10); exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/lhist_multiple.json")))' EXPECT ^True$ TIMEOUT 5 NAME stats RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @stats = stats(2); @stats = stats(10); exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/stats.json")))' EXPECT ^True$ TIMEOUT 5 NAME multiple stats RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @stats["curl"] = stats(2); @stats["zsh"] = stats(10); exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/stats_multiple.json")))' EXPECT ^True$ TIMEOUT 5 NAME printf RUN {{BPFTRACE}} -q -f json -e 'BEGIN { printf("test %d", 5); exit(); }' EXPECT ^{"type": "printf", "data": "test 5"}$ TIMEOUT 5 NAME printf_escaping RUN {{BPFTRACE}} -q -f json -e 'BEGIN { printf("test \r \n \t \\ \" bar"); exit(); }' EXPECT ^{"type": "printf", "data": "test \\r \\n \\t \\\\ \\\" bar"}$ TIMEOUT 5 NAME time RUN {{BPFTRACE}} -q -f json -e 'BEGIN { time(); exit(); }' EXPECT ^{"type": "time", "data": "[0-9]*:[0-9]*:[0-9]*\\n"}$ TIMEOUT 5 NAME syscall RUN {{BPFTRACE}} --unsafe -f json -e 'BEGIN { system("echo a b c"); exit(); }' EXPECT ^{"type": "syscall", "data": "a b c\\n"}$ TIMEOUT 5 NAME join_delim RUN {{BPFTRACE}} --unsafe -f json -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv, ","); }' -c "./testprogs/syscall execve /bin/echo 'A'" EXPECT ^{"type": "join", "data": "/bin/echo,'A'"} TIMEOUT 5 NAME cat RUN {{BPFTRACE}} -f json -e 'BEGIN { cat("/proc/uptime"); exit(); }' EXPECT ^{"type": "cat", "data": "[0-9]*.[0-9]* [0-9]*.[0-9]*\\n"}$ TIMEOUT 5 # Careful with '[' and ']', they are read by the test engine as a regex # character class, so make sure to escape them. NAME tuple RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @ = (1, 2, "string", (4, 5)); exit(); }' EXPECT ^{"type": "map", "data": {"@": \[1,2,"string",\[4,5\]\]}}$ TIMEOUT 5 NAME tuple_with_struct RUN {{BPFTRACE}} -f json -e 'struct Foo { int m; int n; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); @ = (0, $f); exit(); }' EXPECT ^{"type": "map", "data": {"@": \[0,{ "m": 2, "n": 3 }\]}}$ TIMEOUT 5 AFTER ./testprogs/simple_struct NAME tuple_with_escaped_string RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @ = (1, 2, "string with \"quotes\""); exit(); }' EXPECT ^{"type": "map", "data": {"@": \[1,2,"string with \\"quotes\\""\]}}$ TIMEOUT 5 NAME print_non_map RUN {{BPFTRACE}} -q -f json -e 'BEGIN { $x = 5; print($x); exit() }' EXPECT ^{"type": "value", "data": 5}$ TIMEOUT 1 NAME print_non_map_builtin RUN {{BPFTRACE}} -q -f json -e 'BEGIN { print(comm); exit() }' EXPECT ^{"type": "value", "data": "bpftrace"}$ TIMEOUT 1 NAME print_non_map_tuple RUN {{BPFTRACE}} -q -f json -e 'BEGIN { $t = (1, 2, "string"); print($t); exit() }' EXPECT ^{"type": "value", "data": \[1,2,"string"\]}$ TIMEOUT 1 NAME print_non_map_struct RUN {{BPFTRACE}} -f json -e 'struct Foo { int m; int n; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); print($f); exit(); }' EXPECT ^{"type": "value", "data": { "m": 2, "n": 3 }}$ TIMEOUT 5 AFTER ./testprogs/simple_struct NAME print_non_map_nested_struct RUN {{BPFTRACE}} -f json -e 'struct Foo { struct { int m[1] } y; struct { int n; } a; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); print($f); exit(); }' EXPECT ^{"type": "value", "data": { "y": { "m": \[2\] }, "a": { "n": 3 } }}$ TIMEOUT 5 AFTER ./testprogs/simple_struct NAME print_avg_map_args RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 2, 10); clear(@); exit(); }' EXPECT {"type": "stats", "data": {"@": { *"c": 3, *"d": 4}}} TIMEOUT 1 NAME print_avg_map_with_large_top RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 10, 10); clear(@); exit(); }' EXPECT {"type": "stats", "data": {"@": { *"a": 1, *"b": 2, *"c": 3, *"d": 4}}} TIMEOUT 1 NAME print_hist_with_top_arg RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 2); clear(@); exit(); }' EXPECT {"type": "hist", "data": {"@": {"2": \[{"min": 16, "max": 31, "count": 1}\], "3": \[{"min": 16, "max": 31, "count": 1}\]}}} TIMEOUT 1 NAME print_hist_with_large_top_arg RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 10); clear(@); exit(); }' EXPECT {"type": "hist", "data": {"@": {"1": \[{"min": 8, "max": 15, "count": 1}\], "2": \[{"min": 16, "max": 31, "count": 1}\], "3": \[{"min": 16, "max": 31, "count": 1}\]}}} TIMEOUT 1 bpftrace-0.14.0/tests/runtime/other000066400000000000000000000175451413460502400172660ustar00rootroot00000000000000NAME if_gt PROG i:ms:1 {$a = 10; if ($a > 2) { $a = 20 } printf("a=%d\n", $a); exit();} EXPECT a=20 TIMEOUT 5 NAME if_lt PROG i:ms:1 {$a = 10; if ($a < 2) { $a = 20 } printf("a=%d\n", $a); exit();} EXPECT a=10 TIMEOUT 5 NAME ifelse_go_else PROG i:ms:1 {$a = ""; if (10 < 2) { $a = "hi" } else {$a = "hello"} printf("a=%s\n", $a); exit();} EXPECT a=hello TIMEOUT 5 NAME ifelse_go_if PROG i:ms:1 {$a = ""; if (10 > 2) { $a = "hi" } else {$a = "hello"} printf("a=%s\n", $a); exit();} EXPECT a=hi TIMEOUT 5 NAME ifelseif_go_elseif PROG i:ms:1 {$a = ""; if (1 > 2) { $a = "hi" } else if (2 < 3) { $a = "hello" } printf("a=%s\n", $a); exit();} EXPECT a=hello TIMEOUT 5 NAME ifelseifelse_go_else PROG i:ms:1 {$a = ""; if (1 > 2) { $a = "hi" } else if (5 < 3) { $a = "hello" } else { $a = "asdf" } printf("a=%s\n", $a); exit();} EXPECT a=asdf TIMEOUT 5 NAME if_cast PROG i:ms:1 { if ((int32)pid) {} printf("done\n"); exit();} EXPECT done TIMEOUT 5 NAME ternary PROG i:ms:1 { $a = 1 ? "yes" : "no"; printf("%s\n", $a); exit();} EXPECT yes TIMEOUT 5 NAME ternary_lnot PROG i:ms:1 { $a = !nsecs ? 0 : 1; printf("%d\n", $a); exit() } EXPECT 1 TIMEOUT 5 NAME ternary_int8 PROG i:ms:1 { $a = 1 ? (int8)1 : 0; printf("%d\n", $a); exit();} EXPECT 1 TIMEOUT 5 NAME ternary_none_type PROG i:ms:1 { nsecs ? printf("yes\n") : printf("no") ; exit(); } EXPECT yes TIMEOUT 5 NAME unroll PROG i:ms:1 {$a = 1; unroll (10) { $a = $a + 1; } printf("a=%d\n", $a); exit();} EXPECT a=11 TIMEOUT 5 NAME unroll_max_value PROG i:ms:1 {$a = 1; unroll (101) { $a = $a + 2; } printf("a=%d\n", $a); exit();} EXPECT unroll maximum value is 100 TIMEOUT 5 NAME unroll_min_value PROG i:ms:1 {$a = 1; unroll (0) { $a = $a + 2; } printf("a=%d\n", $a); exit();} EXPECT unroll minimum value is 1 TIMEOUT 5 NAME unroll_param RUN {{BPFTRACE}} -e 'i:ms:1 {$a = 1; unroll ($1) { $a = $a + 1; } printf("a=%d\n", $a); exit();}' 10 EXPECT a=11 TIMEOUT 5 NAME if_compare_and_print_string PROG BEGIN { if (comm == "bpftrace") { printf("comm: %s\n", comm);} exit();} EXPECT comm: bpftrace TIMEOUT 5 NAME struct positional string compare - equal returns true RUN {{BPFTRACE}} -e 'BEGIN { if (str($1) == str($2)) { printf("I got %s\n", str($1));} exit();}' "hello" "hello" EXPECT I got hello TIMEOUT 5 NAME struct positional string compare - equal returns false RUN {{BPFTRACE}} -e 'BEGIN { if (str($1) == str($2)) { printf("I got %s\n", str($1));} else { printf("not equal\n");} exit();}' "hi" "hello" EXPECT not equal TIMEOUT 5 NAME struct positional string compare - not equal RUN {{BPFTRACE}} -e 'BEGIN { if (str($1) != str($2)) { printf("I got %s\n", str($1));} else { printf("not equal\n");} exit();}' "hello" "hello" EXPECT not equal TIMEOUT 5 NAME positional string compare via variable - equal RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1); if ($x == "hello")) { printf("I got hello\n");} else { printf("not equal\n");} exit();}' "hello" EXPECT I got hello TIMEOUT 5 NAME positional string compare via variable - not equal RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1); if ($x == "hello")) { printf("I got hello\n");} else { printf("not equal\n");} exit();}' "hell" EXPECT not equal TIMEOUT 5 NAME positional attachpoint RUN {{BPFTRACE}} -e 'i:ms:$1 { printf("hello world\n"); exit(); }' 1 EXPECT hello world TIMEOUT 1 NAME positional attachpoint probe RUN {{BPFTRACE}} -e 'BEG$1 { printf("hello world\n"); exit(); }' IN EXPECT hello world TIMEOUT 1 NAME string compare map lookup RUN {{BPFTRACE}} -e 't:syscalls:sys_enter_openat /comm == "syscall"/ { @[comm] = 1; }' -c "./testprogs/syscall openat" EXPECT @\[syscall\]: 1 TIMEOUT 5 NAME struct partial string compare - pass RUN {{BPFTRACE}} -e 'BEGIN { if (strncmp(str($1), str($2), 4) == 0) { printf("I got %s\n", str($1));} exit();}' "hhvm" "hhvm-proc" EXPECT I got hhvm TIMEOUT 5 NAME struct partial string compare - pass reverse RUN {{BPFTRACE}} -e 'BEGIN { if (strncmp(str($1), str($2), 4) == 0) { printf("I got %s\n", str($1));} exit();}' "hhvm-proc" "hhvm" EXPECT I got hhvm-proc TIMEOUT 5 NAME strncmp function argument RUN {{BPFTRACE}} -e 'struct F {char s[8];} u:./testprogs/string_args:print { @=strncmp(((struct F*)arg0)->s, "hello", 5); }' -c ./testprogs/string_args EXPECT @: 0 TIMEOUT 5 NAME short non null-terminated string print RUN {{BPFTRACE}} -e 'struct F {char s[5];} u:./testprogs/string_args:print { $a = ((struct F*)arg0)->s; printf("%s %s\n", $a, $a); }' -c ./testprogs/string_args EXPECT hello hello TIMEOUT 5 NAME optional_positional_int PROG BEGIN { printf("-%d-\n", $1); exit() } EXPECT -0- TIMEOUT 1 NAME optional_positional_str PROG BEGIN { printf("-%s-\n", str($1)); exit() } EXPECT -- TIMEOUT 1 NAME positional number as string RUN {{BPFTRACE}} -e 'BEGIN { printf("-%s-\n", str($1)); exit() }' 1 EXPECT -1- TIMEOUT 1 NAME positional as string literal RUN {{BPFTRACE}} -e 'BEGIN { @ = kaddr(str($1)); exit() }' vfs_read EXPECT Attaching 1 probe... TIMEOUT 5 NAME positional arg count RUN {{BPFTRACE}} -e 'BEGIN { printf("got %d args: %s %d\n", $#, str($1), $2); exit();}' "one" 2 EXPECT got 2 args: one 2 TIMEOUT 5 NAME positional multiple bases RUN {{BPFTRACE}} -e 'BEGIN { printf("got: %d %d 0x%x\n", $1, $2, $3); exit() }' 123 0775 0x123 EXPECT got: 123 509 0x123 TIMEOUT 5 NAME positional pointer arithmetics RUN {{BPFTRACE}} -e 'BEGIN { printf("%s", str($1 + 1)); exit(); }' hello EXPECT ello TIMEOUT 1 NAME positional strncmp RUN {{BPFTRACE}} -e 'BEGIN { if (strncmp("hello", "hell", $1) == 0) { printf("ok\n"); } exit(); }' 3 EXPECT ok TIMEOUT 1 NAME positional lhist RUN {{BPFTRACE}} -e 'kretprobe:vfs_read { @bytes = lhist(retval, $1, $2, $3); exit()}' 0 10000 1000 EXPECT @bytes: *\n[\[(].* TIMEOUT 5 AFTER ./testprogs/syscall read NAME positional buf RUN {{BPFTRACE}} -e 'BEGIN { @ = buf("hello", $1); exit(); }' 5 EXPECT @: hello TIMEOUT 1 NAME positional kstack RUN {{BPFTRACE}} -e 'k:do_nanosleep { printf("SUCCESS %s\n%s\n", kstack(), kstack($1)); exit(); }' 1 EXPECT SUCCESS TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME positional ustack RUN {{BPFTRACE}} -e 'k:do_nanosleep { printf("SUCCESS %s\n%s\n", ustack(), ustack($1)); exit(); }' 1 EXPECT SUCCESS TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME lhist can be cleared PROG BEGIN{ @[1] = lhist(3,0,10,1); clear(@); exit() } EXPECT .* TIMEOUT 1 NAME hist can be cleared PROG BEGIN{ @[1] = hist(1); clear(@); exit() } EXPECT .* TIMEOUT 1 NAME stats can be cleared PROG BEGIN{ @[1] = stats(1); clear(@); exit() } EXPECT .* TIMEOUT 1 NAME avg can be cleared PROG BEGIN{ @[1] = avg(1); clear(@); exit() } EXPECT .* TIMEOUT 1 NAME sigint under heavy load RUN {{BPFTRACE}} --unsafe -e 'tracepoint:sched:sched_switch { system("echo foo"); } END { printf("end"); }' EXPECT end TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 2e9; pkill -SIGINT bpftrace NAME bitfield access PROG struct Foo { unsigned int a:4, b:8, c:3, d:1, e:16; } uprobe:./testprogs/bitfield_test:func{ $foo = (struct Foo *)arg0; printf("%d %d %d %d %d\n", $foo->a, $foo->b, $foo->c, $foo->d, $foo->e); exit()} EXPECT 1 2 5 0 65535 TIMEOUT 5 AFTER ./testprogs/bitfield_test NAME bitfield_access_2 PROG struct Bar { short a:4, b:8, c:3, d:1; int e:9, f:15, g:1, h:2, i:5 } uprobe:./testprogs/bitfield_test:func2 { $bar = (struct Bar *)arg0; printf("%d %d %d %d %d", $bar->a, $bar->b, $bar->c, $bar->d, $bar->e); printf(" %d %d %d %d", $bar->f, $bar->g, $bar->h, $bar->i); exit()} EXPECT 1 217 5 1 500 31117 1 2 27 TIMEOUT 5 AFTER ./testprogs/bitfield_test NAME exit exits immediately PROG i:ms:100 { @++; exit(); @++ } EXPECT @: 1 TIMEOUT 1 NAME map_assign_map_ptr PROG i:ms:100 { @ = curtask; @a = @; printf("%d\n", @a); exit(); } EXPECT -?[0-9]+ TIMEOUT 1 NAME runtime_error_check_delete RUN {{BPFTRACE}} -k -e 'i:ms:100 { @[1] = 1; delete(@[2]); exit(); }' EXPECT WARNING: Failed to map_delete_elem: No such file or directory \(-2\) TIMEOUT 1 NAME runtime_error_check_lookup RUN {{BPFTRACE}} -kk -e 'i:ms:100 { @[1] = 1; printf("%d\n", @[2]); exit(); }' EXPECT WARNING: Failed to map_lookup_elem: 0 TIMEOUT 1 bpftrace-0.14.0/tests/runtime/outputs/000077500000000000000000000000001413460502400177315ustar00rootroot00000000000000bpftrace-0.14.0/tests/runtime/outputs/complex.json000066400000000000000000000001021413460502400222640ustar00rootroot00000000000000{"type": "map", "data": { "@complex": { "bpftrace,2": 5} }} bpftrace-0.14.0/tests/runtime/outputs/hist.json000066400000000000000000000007331413460502400215760ustar00rootroot00000000000000{"type": "hist", "data": { "@hist": [ {"min": 2, "max": 3, "count": 1}, {"min": 4, "max": 7, "count": 0}, {"min": 8, "max": 15, "count": 0}, {"min": 16, "max": 31, "count": 0}, {"min": 32, "max": 63, "count": 0}, {"min": 64, "max": 127, "count": 0}, {"min": 128, "max": 255, "count": 0}, {"min": 256, "max": 511, "count": 0}, {"min": 512, "max": 1023, "count": 0}, {"min": 1024, "max": 2047, "count": 1} ] }} bpftrace-0.14.0/tests/runtime/outputs/hist_multiple.json000066400000000000000000000012211413460502400235020ustar00rootroot00000000000000{"type": "hist", "data": { "@": { "bpftrace": [ {"min": 2, "max": 3, "count": 1} ], "curl": [ {"max": -1, "count": 1}, {"min": 0, "max": 0, "count": 1}, {"min": 1, "max": 1, "count": 0}, {"min": 2, "max": 3, "count": 0}, {"min": 4, "max": 7, "count": 0}, {"min": 8, "max": 15, "count": 0}, {"min": 16, "max": 31, "count": 0}, {"min": 32, "max": 63, "count": 0}, {"min": 64, "max": 127, "count": 0}, {"min": 128, "max": 255, "count": 0}, {"min": 256, "max": 511, "count": 1}, {"min": 512, "max": 1023, "count": 0}, {"min": 1024, "max": 2047, "count": 2} ]} }} bpftrace-0.14.0/tests/runtime/outputs/lhist.json000066400000000000000000000007571413460502400217600ustar00rootroot00000000000000{"type": "hist", "data": { "@h": [ {"min": 0, "max": 9, "count": 1}, {"min": 10, "max": 19, "count": 0}, {"min": 20, "max": 29, "count": 0}, {"min": 30, "max": 39, "count": 0}, {"min": 40, "max": 49, "count": 0}, {"min": 50, "max": 59, "count": 1}, {"min": 60, "max": 69, "count": 0}, {"min": 70, "max": 79, "count": 0}, {"min": 80, "max": 89, "count": 0}, {"min": 90, "max": 99, "count": 0}, {"min": 100, "count": 1} ] }} bpftrace-0.14.0/tests/runtime/outputs/lhist_multiple.json000066400000000000000000000011001413460502400236520ustar00rootroot00000000000000{"type": "hist", "data": { "@stats": { "curl": [ {"min": 50, "max": 59, "count": 1} ], "bpftrace": [ {"min": 0, "max": 9, "count": 1}, {"min": 10, "max": 19, "count": 0}, {"min": 20, "max": 29, "count": 0}, {"min": 30, "max": 39, "count": 0}, {"min": 40, "max": 49, "count": 0}, {"min": 50, "max": 59, "count": 0}, {"min": 60, "max": 69, "count": 0}, {"min": 70, "max": 79, "count": 0}, {"min": 80, "max": 89, "count": 0}, {"min": 90, "max": 99, "count": 0}, {"min": 100, "count": 1} ]} }} bpftrace-0.14.0/tests/runtime/outputs/map.json000066400000000000000000000001071413460502400213770ustar00rootroot00000000000000{"type": "map", "data": { "@map": { "key1": 2, "key2": 3} }} bpftrace-0.14.0/tests/runtime/outputs/scalar.json000066400000000000000000000000541413460502400220700ustar00rootroot00000000000000{"type": "map", "data": { "@scalar": 5 }} bpftrace-0.14.0/tests/runtime/outputs/scalar_str.json000066400000000000000000000000731413460502400227610ustar00rootroot00000000000000{"type": "map", "data": { "@scalar_str": "a b \n d e" }} bpftrace-0.14.0/tests/runtime/outputs/stats.json000066400000000000000000000001231413460502400217560ustar00rootroot00000000000000{"type": "stats", "data": { "@stats": {"count": 2, "average": 6, "total": 12} }} bpftrace-0.14.0/tests/runtime/outputs/stats_multiple.json000066400000000000000000000002261413460502400236750ustar00rootroot00000000000000{"type": "stats", "data": { "@stats": { "curl": {"count": 1, "average": 2, "total": 2}, "zsh": {"count": 1, "average": 10, "total": 10}} }} bpftrace-0.14.0/tests/runtime/pointers000066400000000000000000000040061413460502400177740ustar00rootroot00000000000000NAME u8 pointer increment PROG BEGIN { @=(int8*) 32; @+=1; exit(); } EXPECT ^@: 33 TIMEOUT 5 NAME u16 pointer increment PROG BEGIN { @=(int16*) 32; @+=1; exit(); } EXPECT ^@: 34 TIMEOUT 5 NAME u32 pointer increment PROG BEGIN { @=(int32*) 32; @+=1; exit(); } EXPECT ^@: 36 TIMEOUT 5 NAME u64 pointer increment PROG BEGIN { @=(int64*) 32; @+=1; exit(); } EXPECT ^@: 40 TIMEOUT 5 NAME u8 pointer unop post increment PROG BEGIN { @=(int8*) 32; @++; exit(); } EXPECT ^@: 33 TIMEOUT 5 NAME u16 pointer unop post increment PROG BEGIN { @=(int16*) 32; @++; exit(); } EXPECT ^@: 34 TIMEOUT 5 NAME u32 pointer unop post increment PROG BEGIN { @=(int32*) 32; @++; exit(); } EXPECT ^@: 36 TIMEOUT 5 NAME u64 pointer unop post increment PROG BEGIN { @=(int64*) 32; @++; exit(); } EXPECT ^@: 40 TIMEOUT 5 NAME u8 pointer unop pre increment PROG BEGIN { @=(int8*) 32; @++; exit(); } EXPECT ^@: 33 TIMEOUT 5 NAME u16 pointer unop pre increment PROG BEGIN { @=(int16*) 32; @++; exit(); } EXPECT ^@: 34 TIMEOUT 5 NAME u32 pointer unop pre increment PROG BEGIN { @=(int32*) 32; @++; exit(); } EXPECT ^@: 36 TIMEOUT 5 NAME u64 pointer unop pre increment PROG BEGIN { @=(int64*) 32; @++; exit(); } EXPECT ^@: 40 TIMEOUT 5 NAME Pointer decrement 1 PROG BEGIN { @=(int32*) 32; @-=1; exit(); } EXPECT ^@: 28 TIMEOUT 5 NAME Pointer decrement PROG BEGIN { @=(int32*) 32; @--; exit(); } EXPECT ^@: 28 TIMEOUT 5 NAME Pointer increment 6 PROG BEGIN { @=(int32*) 32; @+=6; exit(); } EXPECT ^@: 56 TIMEOUT 5 NAME Pointer decrement 6 PROG BEGIN { @=(int32*) 32; @-=6; exit(); } EXPECT ^@: 8 TIMEOUT 5 NAME Struct pointer math PROG struct A {int32_t a; int32_t b; char c[28] }; BEGIN { $a=(struct A*) 1024; printf("%lu - 5 x %lu = %lu\n", $a, sizeof(struct A), $a-5); exit(); } EXPECT 1024 - 5 x 36 = 844 TIMEOUT 5 NAME Pointer decrement with map PROG BEGIN { @dec = 4; @=(int32*) 32; @-=@dec; exit(); } EXPECT ^@: 16 TIMEOUT 5 NAME Pointer walk through struct RUN {{BPFTRACE}} runtime/scripts/struct_walk.bt -c ./testprogs/struct_walk EXPECT ^a: 45 b: 1000 TIMEOUT 5 bpftrace-0.14.0/tests/runtime/probe000066400000000000000000000125331413460502400172440ustar00rootroot00000000000000NAME kprobe PROG kprobe:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT SUCCESS kprobe [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kprobe_short_name PROG k:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT SUCCESS kprobe_short_name [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kprobe_target PROG kprobe:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT kprobe probe type requires 1 argument TIMEOUT 5 NAME kprobe_order RUN {{BPFTRACE}} runtime/scripts/kprobe_order.bt EXPECT first second TIMEOUT 5 AFTER /bin/bash -c "./testprogs/syscall nanosleep 1000"; /bin/bash -c "./testprogs/syscall nanosleep 1000" NAME kprobe_offset PROG kprobe:vfs_read+0 { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT SUCCESS kprobe_offset [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read # Note: this test may fail if you've installed a new kernel but not rebooted # yet. Reason is b/c bpftrace will look for a vmlinux based on the running kernel's # version. NAME kprobe_offset_fail_size PROG kprobe:vfs_read+1000000 { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT Offset outside the function bounds \('vfs_read' size is* TIMEOUT 5 NAME kretprobe PROG kretprobe:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT SUCCESS kretprobe [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kretprobe_short_name PROG kr:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT SUCCESS kretprobe_short_name [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kretprobe_target PROG kretprobe:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", pid); exit(); } EXPECT kretprobe probe type requires 1 argument TIMEOUT 5 NAME kretprobe_order RUN {{BPFTRACE}} runtime/scripts/kretprobe_order.bt EXPECT first second TIMEOUT 5 AFTER /bin/bash -c "./testprogs/syscall nanosleep 1000"; /bin/bash -c "./testprogs/syscall nanosleep 1000" NAME uprobe PROG uprobe:/bin/bash:echo_builtin { printf("arg0: %d\n", arg0); exit();} EXPECT arg0: [0-9]* TIMEOUT 5 AFTER /bin/bash -c "echo lala" NAME uprobe_offset PROG uprobe:/bin/bash:echo_builtin+0 { printf("arg0: %d\n", arg0); exit();} EXPECT arg0: [0-9]* TIMEOUT 5 AFTER /bin/bash -c "echo lala" NAME uprobe_offset PROG uprobe:/bin/bash:"echo_builtin"+0 { printf("arg0: %d\n", arg0); exit();} EXPECT arg0: [0-9]* TIMEOUT 5 AFTER /bin/bash -c "echo lala" NAME uprobe_offset_fail_size PROG uprobe:/bin/bash:echo_builtin+1000000 { printf("arg0: %d\n", arg0); exit();} EXPECT Offset outside the function bounds \('echo_builtin' size is* TIMEOUT 5 NAME uprobe_address_fail_resolve PROG uprobe:/bin/bash:10 { printf("arg0: %d\n", arg0); exit();} EXPECT Could not resolve address: /bin/bash:0xa TIMEOUT 5 NAME uprobe_library PROG uprobe:libc:fread { printf("size: %d\n", arg1); exit(); } EXPECT size: [0-9]+ TIMEOUT 5 AFTER ./testprogs/uprobe_library REQUIRES_FEATURE libpath_resolv NAME uprobe_order RUN {{BPFTRACE}} runtime/scripts/uprobe_order.bt EXPECT first second TIMEOUT 5 AFTER /bin/bash -c "echo lala"; /bin/bash -c "echo lala" NAME uretprobe PROG uretprobe:/bin/bash:echo_builtin { printf("readline: %d\n", retval); exit();} EXPECT readline: [0-9]* TIMEOUT 5 AFTER /bin/bash -c "echo lala" NAME uretprobe_order RUN {{BPFTRACE}} runtime/scripts/uretprobe_order.bt EXPECT first second TIMEOUT 5 AFTER /bin/bash -c "echo lala"; /bin/bash -c "echo lala" NAME tracepoint PROG tracepoint:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", gid); exit(); } EXPECT SUCCESS tracepoint [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 TIMEOUT 5 NAME tracepoint_short_name PROG t:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", gid); exit(); } EXPECT SUCCESS tracepoint_short_name [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 TIMEOUT 5 NAME tracepoint_order RUN {{BPFTRACE}} runtime/scripts/tracepoint_order.bt EXPECT first second TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8; ./testprogs/syscall nanosleep 1e8 NAME tracepoint_expansion RUN {{BPFTRACE}} -e 'tracepoint:syscalls:sys_*_nanosleep { printf("hit "); }' -c "./testprogs/syscall nanosleep 1e8" EXPECT hit hit TIMEOUT 5 # Test that we get at least two characters out NAME tracepoint_data_loc PROG tracepoint:irq:irq_handler_entry { print(str(args->name)); exit(); } EXPECT ..+ TIMEOUT 5 NAME profile PROG profile:hz:599 { @[tid] = count(); exit();} EXPECT \@\[[0-9]*\]\:\s[0-9] TIMEOUT 5 NAME profile_short_name PROG p:hz:599 { @[tid] = count(); exit();} EXPECT \@\[[0-9]*\]\:\s[0-9] TIMEOUT 5 NAME interval PROG t:raw_syscalls:sys_enter { @syscalls = count(); } interval:ms:1{ print(@syscalls); clear(@syscalls); exit();} EXPECT @syscalls\:\s[0-9]* TIMEOUT 5 NAME interval_short_name PROG t:raw_syscalls:sys_enter { @syscalls = count(); } i:ms:1{ print(@syscalls); clear(@syscalls); exit();} EXPECT @syscalls\:\s[0-9]* TIMEOUT 5 NAME software PROG software:faults:1 { @[comm] = count(); exit();} EXPECT @\[.*\]\:\s[0-9]* TIMEOUT 5 NAME software_order RUN {{BPFTRACE}} runtime/scripts/software_order.bt EXPECT first second TIMEOUT 5 NAME hardware REQUIRES ls /sys/devices/cpu/events/cache-misses PROG hardware:cache-misses:10 { @[pid] = count(); exit(); } EXPECT @\[.*\]\:\s[0-9]* TIMEOUT 5 NAME BEGIN PROG BEGIN { printf("Hello\n"); exit();} EXPECT Hello TIMEOUT 2 NAME END_processing_after_exit RUN {{BPFTRACE}} -e "interval:s:1 { exit(); } END { printf("end"); }" EXPECT end TIMEOUT 2 bpftrace-0.14.0/tests/runtime/regression000066400000000000000000000056401413460502400203160ustar00rootroot00000000000000# https://github.com/iovisor/bpftrace/pull/566#issuecomment-487295113 NAME codegen_struct_save_nested PROG struct Foo { int m; struct { int x; int y; } bar; int n; } BEGIN { @foo = (struct Foo)0; @bar = @foo.bar; @x = @foo.bar.x; printf("done\n"); exit(); } EXPECT done TIMEOUT 1 NAME logical_and_or_different_sizes PROG struct Foo { int m; } uprobe:./testprogs/simple_struct:func { $foo = (struct Foo*)arg0; printf("%d %d %d %d %d\n", $foo->m, $foo->m && 0, 1 && $foo->m, $foo->m || 0, 0 || $foo->m); exit(); } AFTER ./testprogs/simple_struct EXPECT 2 0 1 1 1 TIMEOUT 5 # https://github.com/iovisor/bpftrace/issues/759 # Update test once https://github.com/iovisor/bpftrace/pull/781 lands # NAME ntop_map_printf # RUN {{BPFTRACE}} -e 'union ip { char v4[4]; char v6[16]; } uprobe:./testprogs/ntop:test_ntop { @a = ntop(2, ((union ip*)arg0)->v4); printf("v4: %s; ", @a); @b = ntop(10, ((union ip*)arg1)->v6); exit() } END { printf("v6: %s", @b); clear(@a); clear(@b) }' # AFTER ./testprogs/ntop # EXPECT v4: 127.0.0.1; v6: ffee:ffee:ddcc:ddcc:bbaa:bbaa:c0a8:1 # TIMEOUT 1 NAME modulo_operator PROG BEGIN { @x = 4; printf("%d\n", @x % 2); exit() } EXPECT 0 TIMEOUT 5 NAME c_array_indexing RUN {{BPFTRACE}} -e 'struct Foo { int a; uint8_t b[10]; } uprobe:testprogs/uprobe_test:function2 { $foo = (struct Foo *)arg0; printf("%c %c %c %c %c\n", $foo->b[0], $foo->b[1], $foo->b[2], $foo->b[3], $foo->b[4]) }' -c ./testprogs/uprobe_test EXPECT h e l l o TIMEOUT 5 # https://github.com/iovisor/bpftrace/issues/1773 NAME single_arg_wildcard_listing RUN {{BPFTRACE}} -l "*do_nanosleep*" EXPECT kprobe:do_nanosleep TIMEOUT 1 NAME single_arg_wildcard_listing_tracepoint RUN {{BPFTRACE}} -l "*sched_switch*" EXPECT tracepoint:sched:sched_switch TIMEOUT 1 # https://github.com/iovisor/bpftrace/issues/1952 NAME async_id_invalid_probe_expansion PROG kprobe:zzzzz { probe; printf("asdf\n") } BEGIN { printf("_%s_\n", "success"); exit() } EXPECT _success_ TIMEOUT 1 NAME strncmp_n_longer_than_buffer PROG BEGIN { printf("_%d_\n", strncmp("wow", comm, 17)); exit() } EXPECT _1_ TIMEOUT 1 NAME strncmp_checks_for_nul PROG BEGIN { printf("_%d_\n", strncmp("one", "one ", 4)); exit() } EXPECT _1_ TIMEOUT 1 NAME string compare with empty PROG BEGIN { if ("hello" == "") { printf("equal"); } else { printf("not equal"); } exit(); } EXPECT not equal TIMEOUT 3 NAME string compare with prefix PROG BEGIN { if ("hello" == "hell") { printf("equal"); } else { printf("not equal"); } exit(); } EXPECT not equal TIMEOUT 3 NAME strncmp with prefix PROG BEGIN { if (strncmp("hello", "hell") == 0) { printf("equal"); } else { printf("not equal"); } exit(); } EXPECT not equal TIMEOUT 3 # The issue this test catches is the verifier doesn't allow multiple ALU ops on ctx pointer. # ctx+const is ok, but ctx+const+const is not ok. NAME access ctx struct field twice PROG tracepoint:sched:sched_wakeup { if (args->comm == "asdf") { print(args->comm) } exit(); } EXPECT Attaching 1 probe TIMEOUT 1 bpftrace-0.14.0/tests/runtime/scripts/000077500000000000000000000000001413460502400176755ustar00rootroot00000000000000bpftrace-0.14.0/tests/runtime/scripts/hello_world.bt000066400000000000000000000000641413460502400225360ustar00rootroot00000000000000BEGIN { printf("hello world!\n"); exit(); } bpftrace-0.14.0/tests/runtime/scripts/interval_order.bt000066400000000000000000000006201413460502400232410ustar00rootroot00000000000000interval:ms:1 { @probe1_ready = 1; if (@go == 1) { printf("first"); } } interval:ms:1 { @probe2_ready = 1; if (@go == 1) { printf(" second\n"); exit(); } } interval:ms:1 { // Make sure all probes are attached before letting them print anything // so that the output we get is all from the same event. if (@probe1_ready == 1 && @probe2_ready == 1) { @go = 1; } } bpftrace-0.14.0/tests/runtime/scripts/kprobe_order.bt000066400000000000000000000006611413460502400227040ustar00rootroot00000000000000kprobe:hrtimer_nanosleep { @probe1_ready = 1; if (@go == 1) { printf("first"); } } kprobe:hrtimer_nanosleep { @probe2_ready = 1; if (@go == 1) { printf(" second\n"); exit(); } } kprobe:hrtimer_nanosleep { // Make sure all probes are attached before letting them print anything // so that the output we get is all from the same event. if (@probe1_ready == 1 && @probe2_ready == 1) { @go = 1; } } bpftrace-0.14.0/tests/runtime/scripts/kretprobe_order.bt000066400000000000000000000006721413460502400234210ustar00rootroot00000000000000kretprobe:hrtimer_nanosleep { @probe1_ready = 1; if (@go == 1) { printf("first"); } } kretprobe:hrtimer_nanosleep { @probe2_ready = 1; if (@go == 1) { printf(" second\n"); exit(); } } kretprobe:hrtimer_nanosleep { // Make sure all probes are attached before letting them print anything // so that the output we get is all from the same event. if (@probe1_ready == 1 && @probe2_ready == 1) { @go = 1; } } bpftrace-0.14.0/tests/runtime/scripts/software_order.bt000066400000000000000000000006341413460502400232540ustar00rootroot00000000000000software:faults:1 { @probe1_ready = 1; if (@go == 1) { printf("first"); } } software:faults:1 { @probe2_ready = 1; if (@go == 1) { printf(" second\n"); exit(); } } software:faults:1 { // Make sure all probes are attached before letting them print anything // so that the output we get is all from the same event. if (@probe1_ready == 1 && @probe2_ready == 1) { @go = 1; } } bpftrace-0.14.0/tests/runtime/scripts/struct_array.bt000077500000000000000000000013231413460502400227500ustar00rootroot00000000000000struct T { uint32_t a; uint32_t b[2]; }; struct W { uint32_t a; struct T t; }; struct C { uint32_t a; void * b; struct W w[10]; }; uprobe:./testprogs/struct_array:clear { $c = (struct C *) arg0; printf("%d ", $c->w[0].a); printf("%d ", $c->w[2].a); printf("%d ", $c->w[4].a); printf("%d ", $c->w[6].a); printf("%d ", $c->w[8].a); printf("-- "); printf("%d ", $c->w[1].t.a); printf("%d ", $c->w[3].t.a); printf("%d ", $c->w[5].t.a); printf("%d ", $c->w[7].t.a); printf("%d ", $c->w[9].t.a); printf("-- "); printf("%d ", $c->w[0].t.b[1]); printf("%d ", $c->w[1].t.b[1]); printf("%d ", $c->w[2].t.b[1]); printf("%d ", $c->w[3].t.b[1]); printf("%d\n", $c->w[4].t.b[1]); }bpftrace-0.14.0/tests/runtime/scripts/struct_array_vars.bt000077500000000000000000000014411413460502400240040ustar00rootroot00000000000000struct T { uint32_t a; uint32_t b[2]; }; struct W { uint32_t a; struct T t; }; struct C { uint32_t a; void * b; struct W w[10]; }; uprobe:./testprogs/struct_array:clear { $c = (struct C *) arg0; $w = $c->w; @w[0] = $c->w; @[$c->w] = 42; printf("%d ", $w[0].a); printf("%d ", $w[2].a); printf("%d ", $w[4].a); printf("%d ", $w[6].a); printf("%d ", $w[8].a); printf("-- "); printf("%d ", @w[0][1].t.a); printf("%d ", @w[0][3].t.a); printf("%d ", @w[0][5].t.a); printf("%d ", @w[0][7].t.a); printf("%d ", @w[0][9].t.a); printf("-- "); printf("%d ", @w[0][0].t.b[1]); printf("%d ", @w[0][1].t.b[1]); printf("%d ", @w[0][2].t.b[1]); printf("%d ", @w[0][3].t.b[1]); printf("%d ", @w[0][4].t.b[1]); printf("-- "); printf("%d ", @[$c->w]); }bpftrace-0.14.0/tests/runtime/scripts/struct_walk.bt000066400000000000000000000006331413460502400225700ustar00rootroot00000000000000struct C { uint32_t a; uint64_t b; }; uprobe:./testprogs/struct_walk:clear { $c = (struct C *) arg0; $size = (uint32) arg1; if ($size != 10) { print("Size should be 10"); exit(); } /* Verifier doesn't know size is always 10 * we have to clamp it */ $i = 10; while ($i-- > 0) { @a += $c->a; @b += ($c++)->b; } printf("a: %d b: %d\n", @a, @b); clear(@a); clear(@b); } bpftrace-0.14.0/tests/runtime/scripts/tracepoint_order.bt000066400000000000000000000007331413460502400235720ustar00rootroot00000000000000tracepoint:syscalls:sys_exit_nanosleep { @probe1_ready = 1; if (@go == 1) { printf("first"); } } tracepoint:syscalls:sys_exit_nanosleep { @probe2_ready = 1; if (@go == 1) { printf(" second\n"); exit(); } } tracepoint:syscalls:sys_exit_nanosleep { // Make sure all probes are attached before letting them print anything // so that the output we get is all from the same event. if (@probe1_ready == 1 && @probe2_ready == 1) { @go = 1; } } bpftrace-0.14.0/tests/runtime/scripts/uprobe_order.bt000066400000000000000000000007001413460502400227100ustar00rootroot00000000000000uprobe:/bin/bash:echo_builtin { @probe1_ready = 1; if (@go == 1) { printf("first"); } } uprobe:/bin/bash:echo_builtin { @probe2_ready = 1; if (@go == 1) { printf(" second\n"); exit(); } } uprobe:/bin/bash:echo_builtin { // Make sure all probes are attached before letting them print anything // so that the output we get is all from the same event. if (@probe1_ready == 1 && @probe2_ready == 1) { @go = 1; } } bpftrace-0.14.0/tests/runtime/scripts/uretprobe_order.bt000066400000000000000000000007111413460502400234250ustar00rootroot00000000000000uretprobe:/bin/bash:echo_builtin { @probe1_ready = 1; if (@go == 1) { printf("first"); } } uretprobe:/bin/bash:echo_builtin { @probe2_ready = 1; if (@go == 1) { printf(" second\n"); exit(); } } uretprobe:/bin/bash:echo_builtin { // Make sure all probes are attached before letting them print anything // so that the output we get is all from the same event. if (@probe1_ready == 1 && @probe2_ready == 1) { @go = 1; } } bpftrace-0.14.0/tests/runtime/scripts/usdt_file_activation_multiprocess.bt000066400000000000000000000006611413460502400272370ustar00rootroot00000000000000BEGIN { @count = 0; } usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { // Ensure each process only increments @count once if (@pids[pid] == 0) { @count++; @pids[pid] = 1; } // Hacky, but easiest way to get script to exit quickly for tests if (@count == 2) { exit(); } } END { printf("found %d processes\n", @count); clear(@pids); clear(@count); } bpftrace-0.14.0/tests/runtime/scripts/usdt_multi_modules.bt000066400000000000000000000001751413460502400241500ustar00rootroot00000000000000usdt:./testprogs/usdt_test:tracetest:testprobe { exit(); } usdt:./testprogs/usdt_sized_args:test:probe1 { exit(); } bpftrace-0.14.0/tests/runtime/scripts/watchpoint_multiattach.bt000066400000000000000000000002641413460502400250050ustar00rootroot00000000000000BEGIN { @count = 0; } watchpoint:increment_0+arg0:4:w, watchpoint:increment_1+arg0:4:w, watchpoint:increment_2+arg0:4:w { @count++; } END { printf("count=%d\n", @count); } bpftrace-0.14.0/tests/runtime/scripts/watchpoint_unwatch.bt000066400000000000000000000003521413460502400241350ustar00rootroot00000000000000BEGIN { @count = 0; } uprobe:./testprogs/watchpoint_unwatch:increment { @addr = (uint64)arg0; } watchpoint:increment+arg0:4:w { printf("hello world\n"); @count++; unwatch(@addr); } END { printf("count=%d\n", @count); } bpftrace-0.14.0/tests/runtime/sigint000066400000000000000000000005551413460502400174330ustar00rootroot00000000000000NAME strace no quit RUN {{BPFTRACE}} -e 'i:s:1 { printf("%s %d\n", "SUCCESS", 1); exit() }' & (./testprogs/syscall nanosleep 5e8 && strace -p $! -o /dev/null) EXPECT SUCCESS 1 TIMEOUT 3 NAME sigint quit RUN {{BPFTRACE}} -e 'END { printf("%s %d", "SUCCESS", 1) }' & (./testprogs/syscall nanosleep 1e9 && /usr/bin/env kill -s SIGINT $!) EXPECT ^SUCCESS 1$ TIMEOUT 2 bpftrace-0.14.0/tests/runtime/signed_ints000066400000000000000000000015121413460502400204360ustar00rootroot00000000000000NAME stats with negative values PROG BEGIN { @=stats(-10); @=stats(-5); @=stats(5); exit() } EXPECT ^@:\scount\s3,\saverage\s-3+,\stotal\s-10$ TIMEOUT 5 NAME avg with negative values PROG BEGIN { @=avg(-30); @=avg(-10); exit(); } EXPECT ^@:\s-20$ TIMEOUT 5 NAME negative map value PROG BEGIN { @ = -11; exit(); } EXPECT @: -11$ TIMEOUT 1 NAME sum negative maps PROG BEGIN { @ = -11; @+=@; exit() } EXPECT @: -22$ TIMEOUT 1 NAME Comparison should print as 0 or 1 PROG struct x { uint64_t x; }; BEGIN { $a = (*(struct x*)0).x; printf("%d %d\n", $a > -1, $a < 1); exit(); } EXPECT ^0 1$ TIMEOUT 1 NAME sum with negative value PROG BEGIN { @=sum(10); @=sum(-20); exit(); } EXPECT ^@: -10$ TIMEOUT 1 NAME mixed values PROG BEGIN { printf("%d %d %d %d\n", (int8) -10, -5555, (int16)-123, 100); exit(); } EXPECT ^-10 -5555 -123 100$ TIMEOUT 5 bpftrace-0.14.0/tests/runtime/struct000066400000000000000000000034051413460502400174570ustar00rootroot00000000000000NAME struct assignment into variable PROG struct Foo { int m; } u:./testprogs/simple_struct:func { $s = *((struct Foo *)arg0); printf("Result: %d\n", $s.m); exit(); } EXPECT Result: 2 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct assignment into map PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s = *((struct Foo *)arg0); exit(); } EXPECT @s: { .m = 2, .n = 3 } TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct field order PROG struct Foo { int n; int m; } u:./testprogs/simple_struct:func { @s = *((struct Foo *)arg0); exit(); } EXPECT @s: { .n = 2, .m = 3 } TIMEOUT 4 AFTER ./testprogs/simple_struct NAME nested struct assignment into map PROG struct Foo { struct { int m[1] } y; struct { int n } a; } u:./testprogs/simple_struct:func { @s = *((struct Foo *)arg0); exit(); } EXPECT @s: { .y = { .m = \[2\] }, .a = { .n = 3 } } TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct as a map key PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s[*((struct Foo *)arg0)] = 0; exit(); } EXPECT @s\[{.m=2,.n=3}\]: 0 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct as a part of multikey PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s[*((struct Foo *)arg0), 42] = 0; exit(); } EXPECT @s\[{.m=2,.n=3}, 42\]: 0 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct as a map key assigned from another map PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @x = *((struct Foo *)arg0); @s[@x] = 0; exit(); } EXPECT @s\[{.m=2,.n=3}\]: 0 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct in a tuple PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s = (1, *((struct Foo *)arg0)); exit(); } EXPECT @s: \(1, { .m = 2, .n = 3 }\) TIMEOUT 4 AFTER ./testprogs/simple_struct bpftrace-0.14.0/tests/runtime/tuples000066400000000000000000000053761413460502400174600ustar00rootroot00000000000000NAME basic tuple PROG BEGIN { $v = 99; $t = (0, 1, "str", (5, 6), $v); printf("%d %d %s %d %d %d\n", $t.0, $t.1, $t.2, $t.3.0, $t.3.1, $t.4); exit(); } EXPECT 0 1 str 5 6 99 TIMEOUT 5 NAME basic tuple map PROG BEGIN { $v = 99; @t = (0, 1, "str"); printf("%d %d %s\n", @t.0, @t.1, @t.2); exit(); } EXPECT 0 1 str TIMEOUT 5 NAME mixed int tuple map PROG BEGIN { @ = ( (int32) -100, (int8) 10, 50 ); exit();} EXPECT @: \(-100, 10, 50\) TIMEOUT 5 NAME mixed int tuple map 2 PROG BEGIN { @ = ( -100, (int8) 10, (int32) 50 ); exit();} EXPECT @: \(-100, 10, 50\) TIMEOUT 5 NAME mixed int tuple map 3 PROG BEGIN { @ = ( -100, (int8) 10, (int32) 50, 100 ); exit();} EXPECT @: \(-100, 10, 50, 100\) TIMEOUT 5 NAME complex tuple 1 PROG BEGIN { print(((int8)-100, (int8) 100, "abcdef", 3, (int32) 1, (int64)-10, (int8)10, (int16)-555)); exit(); } EXPECT \(-100, 100, abcdef, 3, 1, -10, 10, -555\) TIMEOUT 5 NAME tuple struct sizing 1 PROG BEGIN { $t = ((int8) 1, (int64) 1, (int8) 1, (int64) 1); print(sizeof($t)); exit() } EXPECT 32 TIMEOUT 5 NAME tuple struct sizing 2 PROG BEGIN { $t = ((int8) 1, (int16) 1, (int32) 1); print(sizeof($t)); exit() } EXPECT 8 TIMEOUT 5 NAME tuple struct sizing 3 PROG BEGIN { $t = ((int32) 1, (int16) 1, (int8) 1); print(sizeof($t)); exit() } EXPECT 8 TIMEOUT 5 NAME complex tuple 4 PROG BEGIN { $a = ((int8)-100, (int8) 100, "abcdef", 3, (int32) 1, (int64)-10, (int8)10, (int16)-555, "abc"); print(sizeof($a)); exit(); } EXPECT 168 TIMEOUT 5 NAME struct in tuple PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @t = (1, *((struct Foo *)arg0)); exit(); } EXPECT @t: \(1, \{ .m = 2, .n = 3 \}\) TIMEOUT 5 AFTER ./testprogs/simple_struct NAME struct in tuple sizing PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { $t = ((int32)1, *((struct Foo *)arg0)); print(sizeof($t)); exit(); } EXPECT 12 TIMEOUT 5 AFTER ./testprogs/simple_struct NAME array in tuple PROG struct A { int x[4]; } u:./testprogs/array_access:test_struct { @t = (1, ((struct A *)arg0)->x); exit(); } EXPECT @t: \(1, \[1,2,3,4\]\) TIMEOUT 5 AFTER ./testprogs/array_access NAME array in tuple sizing PROG struct A { int x[4]; } u:./testprogs/array_access:test_struct { $t = ((int32)1, ((struct A *)arg0)->x); print(sizeof($t)); exit(); } EXPECT 20 TIMEOUT 5 AFTER ./testprogs/array_access NAME nested tuple PROG BEGIN{ @ = ((int8)1, ((int8)-20, (int8)30)); exit(); } EXPECT @: \(1, \(-20, 30\)\) TIMEOUT 5 # Careful with '(' and ')', they are read by the test engine as a regex group, # so make sure to escape them. NAME tuple print PROG BEGIN { @ = (1, 2, "string", (4, 5)); exit(); } EXPECT ^@: \(1, 2, string, \(4, 5\)\)$ TIMEOUT 5 NAME tuple strftime type is packed PROG BEGIN { @ = (nsecs, strftime("%M:%S", nsecs)); exit(); } EXPECT ^@: \(\d+, \d+:\d+\)$ TIMEOUT 5 bpftrace-0.14.0/tests/runtime/uprobe000066400000000000000000000043741413460502400174350ustar00rootroot00000000000000NAME "uprobes - list probes by file" RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test:*' EXPECT uprobe:./testprogs/uprobe_test:function1 TIMEOUT 5 NAME "uprobes - list probes by file with wildcarded filter" RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test:func*' EXPECT uprobe:./testprogs/uprobe_test:function1 TIMEOUT 5 NAME "uprobes - list probes with wildcarded file matching multiple files" RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe*:*' EXPECT uprobe:./testprogs/uprobe_test:function1 TIMEOUT 5 NAME "uprobes - list probes by file with wildcarded file and filter" RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test*:func*' EXPECT uprobe:./testprogs/uprobe_test:function1 TIMEOUT 5 NAME "uprobes - list probes by file with specific filter" RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test:function1' EXPECT uprobe:./testprogs/uprobe_test:function1 TIMEOUT 5 NAME "uprobes - list probes by file with wildcarded probe type" RUN {{BPFTRACE}} -l '*:./testprogs/uprobe_test:*' | grep -e '^uprobe:' EXPECT uprobe:./testprogs/uprobe_test:function1 TIMEOUT 5 NAME "uprobes - list probes by pid" RUN {{BPFTRACE}} -l -p {{BEFORE_PID}} | grep -e '^uprobe' EXPECT uprobe:.*/testprogs/uprobe_test:function1 TIMEOUT 5 BEFORE ./testprogs/uprobe_test NAME "uprobes - list probes by pid; uprobes only" RUN {{BPFTRACE}} -l 'uprobe:*' -p {{BEFORE_PID}} EXPECT uprobe:.*/testprogs/uprobe_test:function1 TIMEOUT 5 BEFORE ./testprogs/uprobe_test NAME "uprobes - list probes by pid in separate mount namespace" RUN {{BPFTRACE}} -l -p {{BEFORE_PID}} | grep -e '^uprobe' EXPECT uprobe:.*/bpftrace-unshare-mountns-test/uprobe_test:function1 TIMEOUT 5 BEFORE ./testprogs/mountns_wrapper uprobe_test NAME "uprobes - attach to probe for executable in separate mount namespace" RUN {{BPFTRACE}} -e 'uprobe:/tmp/bpftrace-unshare-mountns-test/uprobe_test:function1 { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT Attaching 1 probe... TIMEOUT 5 BEFORE ./testprogs/mountns_wrapper uprobe_test NAME "uprobes - list probes in non-executable library" RUN {{BPFTRACE}} -l 'uprobe:./testlibs/libsimple.so:fun' EXPECT uprobe:./testlibs/libsimple.so:fun TIMEOUT 5 NAME "uprobes - probe function in non-executable library" PROG uprobe:./testlibs/libsimple.so:fun {} EXPECT Attaching 1 probe... TIMEOUT 5 bpftrace-0.14.0/tests/runtime/usdt000066400000000000000000000315021413460502400171110ustar00rootroot00000000000000NAME "usdt probes - list probes by file" RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test:*' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe TIMEOUT 5 REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt - list probes by file with wildcarded probe type" RUN {{BPFTRACE}} -l '*:./testprogs/usdt_test:*' | grep -e '^usdt:' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe TIMEOUT 5 NAME "usdt probes - list probes by pid" RUN {{BPFTRACE}} -l -p {{BEFORE_PID}} | grep -e '^usdt:' EXPECT usdt:.*/testprogs/usdt_test:tracetest:testprobe TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - list probes by pid; usdt only" RUN {{BPFTRACE}} -l 'usdt:*' -p {{BEFORE_PID}} EXPECT usdt_test:tracetest:testprobe TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - lists linked library probes by pid" RUN {{BPFTRACE}} -l 'usdt:*' -p $(pidof usdt_lib) EXPECT libusdt_tp.so:tracetestlib:lib_probe_1 TIMEOUT 5 BEFORE ./testprogs/usdt_lib REQUIRES ./testprogs/usdt_lib should_not_skip NAME "usdt probes - filter probes by file on provider" RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test:tracetest2:*' EXPECT usdt:./testprogs/usdt_test:tracetest2:testprobe2 TIMEOUT 5 REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - filter probes by pid on provider" RUN {{BPFTRACE}} -l 'usdt:*:tracetest2:*' -p {{BEFORE_PID}} EXPECT usdt_test:tracetest2:testprobe2 TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - filter probes by wildcard file and wildcard probe name" RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test*:tracetest:test*' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe2 TIMEOUT 5 REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - filter probes by file and wildcard probe name" RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test:tracetest:test*' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe2 TIMEOUT 5 REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - filter probes by pid and wildcard probe name" RUN {{BPFTRACE}} -l 'usdt:*:tracetest:test*' -p {{BEFORE_PID}} EXPECT usdt_test:tracetest:testprobe2 TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - attach to fully specified probe by file" PROG usdt:./testprogs/usdt_test:tracetest:testprobe { printf("here\n" ); exit(); } EXPECT here BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip TIMEOUT 5 NAME "usdt probes - attach to fully specified probe of child" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:tracetest:testprobe { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT here TIMEOUT 5 NAME "usdt probes - attach to fully specified probe by pid" RUN {{BPFTRACE}} -e 'usdt::tracetest:testprobe { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT here BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip TIMEOUT 5 NAME "usdt probes - attach to fully specified library probe by pid" RUN {{BPFTRACE}} -e 'usdt:./testlibs/libusdt_tp.so:tracetestlib:lib_probe_1 { printf("here\n" ); exit(); }' -p $(pidof usdt_lib) BEFORE ./testprogs/usdt_lib EXPECT here TIMEOUT 5 REQUIRES ./testprogs/usdt_lib should_not_skip NAME "usdt probes - all probes by wildcard and file" PROG usdt:./testprogs/usdt_test:* { printf("here\n" ); exit(); } EXPECT Attaching 3 probes... TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - all probes by wildcard and file with child" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT Attaching 3 probes... TIMEOUT 5 # TODO(mmarchini): re-enable this test # This test has two problems: it relies on the latest version of bcc and it # assumes USDTs are coming only from the binary. On Ubuntu, glibc is built with # USDT support, which means it will attach to 43 probes instead of 3. Before # re-enabling this test, we should: # - Skip if bcc doesn't support multiple probes with the same name # - Fix https://github.com/iovisor/bpftrace/issues/565#issuecomment-496731112 # and https://github.com/iovisor/bpftrace/issues/688 NAME "usdt probes - all probes by wildcard and pid" RUN {{BPFTRACE}} -e 'usdt:* { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT Attaching 3 probes... TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES bash -c "exit 1" # REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - attach to probe by wildcard and file" PROG usdt:./testprogs/usdt_test::*probe2 { printf("here\n" ); exit(); } EXPECT Attaching 2 probes... TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - attach to probe by wildcard and file with child" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test::*probe2 { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT Attaching 2 probes... TIMEOUT 5 NAME "usdt probes - attach to probes by wildcard file" PROG usdt:./testprogs/usdt_test*::* { printf("here\n" ); exit(); } EXPECT Attaching 3 probes... BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip TIMEOUT 5 NAME "usdt probes - attach to probes by wildcard file with child" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test*::* { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT Attaching 3 probes... TIMEOUT 5 NAME "usdt probes - attach to probe on multiple files by wildcard" PROG usdt:./testprogs/usdt*::* { printf("here\n" ); exit(); } EXPECT Attaching 41 probes... TIMEOUT 5 NAME "usdt probes - attach to probe on multiple providers by wildcard and pid" RUN {{BPFTRACE}} -e 'usdt:::*probe2 { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT Attaching 2 probes... TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip # TODO(mmarchini): re-enable this test # This test relies on the latest version of bcc. Before re-enabling this test, # we should: # - Skip if bcc doesn't support multiple probes with the same name NAME "usdt probes - attach to probe with args by file" PROG usdt:./testprogs/usdt_test:* { printf("%s\n", str(arg1) ); exit(); } EXPECT Hello world TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES bash -c "exit 1" # REQUIRES ./testprogs/usdt_test should_not_skip # TODO(mmarchini): re-enable this test # This test relies on the latest version of bcc. Before re-enabling this test, # we should: # - Skip if bcc doesn't support multiple probes with the same name NAME "usdt probes - attach to probe with args by pid" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("%s\n", str(arg1) ); exit(); }' -p {{BEFORE_PID}} EXPECT Hello world TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES bash -c "exit 1" # REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - attach to probe with probe builtin and args by file" PROG usdt:./testprogs/usdt_test:* { printf("%lld %s\n", arg0, probe ); exit(); } EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - attach to probe with probe builtin and args by file with child" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("%lld %s\n", arg0, probe ); exit(); }' -c ./testprogs/usdt_test EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe TIMEOUT 5 NAME "usdt probes - attach to probe with probe builtin and args by pid" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("%lld %s\n", arg0, probe ); exit(); }' -p {{BEFORE_PID}} EXPECT usdt_test:tracetest:testprobe TIMEOUT 5 BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/usdt_test should_not_skip NAME "usdt probes - attach to probe with semaphore" RUN {{BPFTRACE}} -e 'usdt::tracetest:testprobe { printf("%s\n", str(arg1) ); exit(); }' -p {{BEFORE_PID}} EXPECT tracetest_testprobe_semaphore: [1-9] TIMEOUT 5 BEFORE ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/usdt_semaphore_test should_not_skip NAME "usdt probes - file based semaphore activation" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { printf("%s\n", str(arg1) ); exit(); }' --usdt-file-activation EXPECT tracetest_testprobe_semaphore: [1-9] TIMEOUT 5 BEFORE ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/usdt_semaphore_test should_not_skip NAME "usdt probes - file based semaphore activation no process" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { exit(); }' --usdt-file-activation EXPECT Failed to find processes running TIMEOUT 5 REQUIRES ./testprogs/usdt_semaphore_test should_not_skip REQUIRES_FEATURE !uprobe_refcount # We should be able to attach a probe even without any running processes # if the kernel handles the semaphore NAME "usdt probes - file based semaphore activation no process, kernel usdt semaphore" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { exit(); }' --usdt-file-activation EXPECT Attaching 1 probe TIMEOUT 5 REQUIRES ./testprogs/usdt_semaphore_test should_not_skip REQUIRES_FEATURE uprobe_refcount # We should be able to activate a semaphore without specifying a PID or # --usdt-file-activation if the kernel handles the semaphore NAME "usdt probes - file based semaphore activation" PROG usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { printf("%s\n", str(arg1) ); exit(); } EXPECT tracetest_testprobe_semaphore: [1-9] TIMEOUT 5 BEFORE ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/usdt_semaphore_test should_not_skip REQUIRES_FEATURE uprobe_refcount NAME "usdt probes - file based semaphore activation multi process" RUN {{BPFTRACE}} runtime/scripts/usdt_file_activation_multiprocess.bt --usdt-file-activation EXPECT found 2 processes TIMEOUT 5 BEFORE ./testprogs/usdt_semaphore_test & ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/usdt_semaphore_test should_not_skip NAME "usdt probes - list probes by pid in separate mountns" RUN {{BPFTRACE}} -l 'usdt:*' -p {{BEFORE_PID}} EXPECT usdt:.*/tmp/bpftrace-unshare-mountns-test/usdt_test:tracetest:testprobe TIMEOUT 5 BEFORE ./testprogs/mountns_wrapper usdt_test # TODO(dalehamel): re-enable this test # This test relies on the latest version of bcc (expected to be released as 0.13.0) # Once we build against this version with USDT fixes, we should re-enable this test. NAME "usdt probes - attach to fully specified probe by pid in separate mountns" RUN {{BPFTRACE}} -e 'usdt:/tmp/bpftrace-unshare-mountns-test/usdt_test:tracetest:testprobe { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT here TIMEOUT 5 BEFORE ./testprogs/mountns_wrapper usdt_test REQUIRES bash -c "exit 1" NAME "usdt quoted probe name" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_quoted_probe:test:"\"probe1\"" { printf("%d\n", arg0); exit(); }' -p {{BEFORE_PID}} EXPECT 1 TIMEOUT 5 BEFORE ./testprogs/usdt_quoted_probe REQUIRES ./testprogs/usdt_quoted_probe should_not_skip NAME "usdt sized arguments" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_sized_args:test:probe2 { printf("%ld\n", arg0); exit(); }' -p {{BEFORE_PID}} EXPECT ^1$ TIMEOUT 5 BEFORE ./testprogs/usdt_sized_args NAME "usdt constant arguments" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:const_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT 4092785136 -202182160 61936 -3600 240 -16 #EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 # Bug in bcc, constants are stored in a 32-bit int, so 64-bit values are truncated TIMEOUT 5 REQUIRES ./testprogs/usdt_args should_not_skip NAME "usdt reg arguments" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:reg_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 TIMEOUT 5 REQUIRES ./testprogs/usdt_args should_not_skip NAME "usdt addr arguments" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:addr_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 TIMEOUT 5 REQUIRES ./testprogs/usdt_args should_not_skip NAME "usdt addr+index arguments" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:index_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 TIMEOUT 5 REQUIRES ./testprogs/usdt_args should_not_skip # USDT probes can be inlined which creates duplicate identical probes. We must # attach to all of them NAME "usdt duplicated markers" RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_inlined:tracetest:testprobe { printf("%d\n", arg1); @a += 1; if (@a >= 2) {exit();} }' -c ./testprogs/usdt_inlined EXPECT Attaching 2 probes.*\n.*\s999\s*100 TIMEOUT 1 REQUIRES ./testprogs/usdt_inlined should_not_skip NAME "usdt probes in multiple modules" RUN {{BPFTRACE}} runtime/scripts/usdt_multi_modules.bt -c ./testprogs/usdt_test EXPECT Attaching 2 probes TIMEOUT 1 REQUIRES ./testprogs/usdt_test should_not_skip bpftrace-0.14.0/tests/runtime/variable000066400000000000000000000033171413460502400177220ustar00rootroot00000000000000NAME global_int PROG i:ms:1 {@a = 10; printf("%d\n", @a); exit();} EXPECT \@a: 10 TIMEOUT 5 NAME global_string PROG i:ms:1 {@a = "hi"; printf("%s\n", @a); exit();} EXPECT @a: hi TIMEOUT 5 NAME global_buf PROG i:ms:1 {@a = buf("hi", 2); printf("%r\n", @a); exit();} EXPECT @a: hi TIMEOUT 5 NAME local_int PROG i:ms:1 {$a = 10; printf("a=%d\n", $a); exit();} EXPECT a=10 TIMEOUT 5 NAME local_string PROG i:ms:1 {$a = "hi"; printf("a=%s\n", $a); exit();} EXPECT a=hi TIMEOUT 5 NAME local_buf PROG i:ms:1 {$a = buf("hi", 2); printf("a=%r\n", $a); exit();} EXPECT a=hi TIMEOUT 5 NAME buf_equality PROG i:ms:1 {$a = buf("hi", 2); $b = buf("bye", 3); printf("equal=%d, unequal=%d\n", $a == $a, $a != $b); exit();} EXPECT equal=1, unequal=1 TIMEOUT 5 NAME global_associative_arrays PROG k:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid] != 0/ { printf("slept for %d ms\n", (nsecs - @start[tid]) / 1000000); delete(@start[tid]); exit();} EXPECT slept for [0-9]+ ms TIMEOUT 5 AFTER ./testprogs/syscall read NAME scratch PROG k:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid] != 0/ { $delta = nsecs - @start[tid]; printf("slept for %d ms\n", $delta / 1000000); delete(@start[tid]); exit(); } EXPECT slept for [0-9]* ms TIMEOUT 5 AFTER ./testprogs/syscall read NAME 32-bit tracepoint arg PROG tracepoint:random:urandom_read { $i = args->got_bits; printf("bits: %d\n", $i); if ($i == 24) { exit() } } EXPECT bits: 24 TIMEOUT 5 AFTER dd if=/dev/urandom bs=3 count=1 NAME tracepoint arg casts in predicates RUN {{BPFTRACE}} -e 'tracepoint:syscalls:sys_enter_wait4 /args->ru/ { @ru[tid] = args->ru; } tracepoint:syscalls:sys_exit_wait4 /@ru[tid]/ { @++; exit(); }' -c ./testprogs/wait4_ru EXPECT @: 1 TIMEOUT 5 bpftrace-0.14.0/tests/runtime/watchpoint000066400000000000000000000042501413460502400203120ustar00rootroot00000000000000NAME watchpoint - absolute address RUN {{BPFTRACE}} -e 'watchpoint::0x10000000:8:w { printf("hit!\n"); exit() }' -c ./testprogs/watchpoint EXPECT hit! ARCH aarch64|ppc64|ppc64le|x86_64 TIMEOUT 5 NAME kwatchpoint - knl absolute address RUN {{BPFTRACE}} -e "watchpoint:0x$(awk '$3 == "jiffies" {print $1}' /proc/kallsyms):4:w { printf(\"hit\n\"); exit(); }" EXPECT hit ARCH aarch64|ppc64|ppc64le|x86_64 TIMEOUT 5 REQUIRES awk '$3 == "jiffies" {got=1} END {exit !got}' /proc/kallsyms NAME function_arg_addr RUN {{BPFTRACE}} -e 'watchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -c ./testprogs/watchpoint_func EXPECT hit! ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal NAME async_function_arg_addr RUN {{BPFTRACE}} -e 'asyncwatchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -c ./testprogs/watchpoint_func EXPECT hit! ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal NAME function_arg_addr_process_flag RUN {{BPFTRACE}} -e 'watchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -p $(pidof watchpoint_func) BEFORE ./testprogs/watchpoint_func EXPECT hit! ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal NAME many_function_probes RUN {{BPFTRACE}} -e 'watchpoint:increment+arg0:4:w { printf("hit!\n") }' -c ./testprogs/watchpoint_func_many_probes EXPECT You are out of watchpoint registers ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal NAME unwatch RUN {{BPFTRACE}} runtime/scripts/watchpoint_unwatch.bt -c ./testprogs/watchpoint_unwatch EXPECT count=1 ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal NAME function_multiattach RUN {{BPFTRACE}} runtime/scripts/watchpoint_multiattach.bt -c ./testprogs/watchpoint_func_wildcard EXPECT count=3 ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal NAME wildcarded_function RUN {{BPFTRACE}} -e 'watchpoint:increment_*+arg0:4:w { printf("hit!\n") }' -c ./testprogs/watchpoint_func_wildcard EXPECT You are out of watchpoint registers ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal NAME unique_probe_bodies RUN {{BPFTRACE}} -e 'watchpoint:increment_*+arg0:4:w { printf("%s!\n", probe) }' -c ./testprogs/watchpoint_func_wildcard EXPECT .*increment_0:4:w! ARCH aarch64|x86_64 TIMEOUT 5 REQUIRES_FEATURE signal bpftrace-0.14.0/tests/semantic_analyser.cpp000066400000000000000000002455031413460502400207410ustar00rootroot00000000000000#include "ast/passes/semantic_analyser.h" #include "ast/passes/field_analyser.h" #include "bpffeature.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace bpftrace { namespace test { namespace semantic_analyser { using ::testing::_; using ::testing::HasSubstr; void test_for_warning( BPFtrace &bpftrace, const std::string &input, const std::string &warning, bool invert = false, bool safe_mode = true) { Driver driver(bpftrace); bpftrace.safe_mode_ = safe_mode; ASSERT_EQ(driver.parse_str(input), 0); ClangParser clang; clang.parse(driver.root_, bpftrace); ASSERT_EQ(driver.parse_str(input), 0); std::stringstream out; // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(true); ast::SemanticAnalyser semantics(driver.root_, bpftrace, out); semantics.analyse(); if (invert) EXPECT_THAT(out.str(), Not(HasSubstr(warning))); else EXPECT_THAT(out.str(), HasSubstr(warning)); } void test_for_warning( const std::string &input, const std::string &warning, bool invert = false, bool safe_mode = true) { auto bpftrace = get_mock_bpftrace(); test_for_warning(*bpftrace, input, warning, invert, safe_mode); } void test(BPFtrace &bpftrace, bool mock_has_features, Driver &driver, const std::string &input, int expected_result = 0, bool safe_mode = true, bool has_child = false, int expected_field_analyser = 0, int expected_parse = 0) { std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; bpftrace.safe_mode_ = safe_mode; ASSERT_EQ(driver.parse_str(input), expected_parse); // Can't continue if parsing failed if (expected_parse) return; ast::FieldAnalyser fields(driver.root_, bpftrace, out); EXPECT_EQ(fields.analyse(), expected_field_analyser) << msg.str() + out.str(); ClangParser clang; clang.parse(driver.root_, bpftrace); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(mock_has_features); ast::SemanticAnalyser semantics(driver.root_, bpftrace, out, has_child); EXPECT_EQ(expected_result, semantics.analyse()) << msg.str() + out.str(); } void test(BPFtrace &bpftrace, const std::string &input, int expected_result=0, bool safe_mode = true) { Driver driver(bpftrace); test(bpftrace, true, driver, input, expected_result, safe_mode); } void test(Driver &driver, const std::string &input, int expected_result=0, bool safe_mode = true) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, true, driver, input, expected_result, safe_mode); } void test(MockBPFfeature &feature, const std::string &input, int expected_result = 0, bool safe_mode = true) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); bool mock_has_features = feature.has_features_; test(*bpftrace, mock_has_features, driver, input, expected_result, safe_mode); } void test(const std::string &input, int expected_result = 0, bool safe_mode = true, bool has_child = false, int expected_field_analyser = 0, int expected_parse = 0) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); test(*bpftrace, true, driver, input, expected_result, safe_mode, has_child, expected_field_analyser, expected_parse); } TEST(semantic_analyser, builtin_variables) { // Just check that each builtin variable exists. test("kprobe:f { pid }", 0); test("kprobe:f { tid }", 0); test("kprobe:f { cgroup }", 0); test("kprobe:f { uid }", 0); test("kprobe:f { username }", 0); test("kprobe:f { gid }", 0); test("kprobe:f { nsecs }", 0); test("kprobe:f { elapsed }", 0); test("kprobe:f { cpu }", 0); test("kprobe:f { curtask }", 0); test("kprobe:f { rand }", 0); test("kprobe:f { ctx }", 0); test("kprobe:f { comm }", 0); test("kprobe:f { kstack }", 0); test("kprobe:f { ustack }", 0); test("kprobe:f { arg0 }", 0); test("kprobe:f { sarg0 }", 0); test("kretprobe:f { retval }", 0); test("kprobe:f { func }", 0); test("uprobe:/bin/sh:f { func }", 0); test("kprobe:f { probe }", 0); test("tracepoint:a:b { args }", 0); test("kprobe:f { fake }", 1); MockBPFfeature feature(false); test(feature, "k:f { cgroup }", 1); } TEST(semantic_analyser, builtin_cpid) { test("i:ms:100 { printf(\"%d\\n\", cpid); }", 1, false, false); test("i:ms:100 { @=cpid }", 1, false, false); test("i:ms:100 { $a=cpid }", 1, false, false); test("i:ms:100 { printf(\"%d\\n\", cpid); }", 0, false, true); test("i:ms:100 { @=cpid }", 0, false, true); test("i:ms:100 { $a=cpid }", 0, false, true); } TEST(semantic_analyser, builtin_functions) { // Just check that each function exists. // Each function should also get its own test case for more thorough testing test("kprobe:f { @x = hist(123) }", 0); test("kprobe:f { @x = lhist(123, 0, 123, 1) }", 0); test("kprobe:f { @x = count() }", 0); test("kprobe:f { @x = sum(pid) }", 0); test("kprobe:f { @x = min(pid) }", 0); test("kprobe:f { @x = max(pid) }", 0); test("kprobe:f { @x = avg(pid) }", 0); test("kprobe:f { @x = stats(pid) }", 0); test("kprobe:f { @x = 1; delete(@x) }", 0); test("kprobe:f { @x = 1; print(@x) }", 0); test("kprobe:f { @x = 1; clear(@x) }", 0); test("kprobe:f { @x = 1; zero(@x) }", 0); test("kprobe:f { time() }", 0); test("kprobe:f { exit() }", 0); test("kprobe:f { str(0xffff) }", 0); test("kprobe:f { buf(0xffff, 1) }", 0); test("kprobe:f { printf(\"hello\\n\") }", 0); test("kprobe:f { system(\"ls\\n\") }", 0, false /* safe_node */); test("kprobe:f { join(0) }", 0); test("kprobe:f { ksym(0xffff) }", 0); test("kprobe:f { usym(0xffff) }", 0); test("kprobe:f { kaddr(\"sym\") }", 0); test("kprobe:f { ntop(0xffff) }", 0); test("kprobe:f { ntop(2, 0xffff) }", 0); #ifdef ARCH_X86_64 test("kprobe:f { reg(\"ip\") }", 0); #endif test("kprobe:f { kstack(1) }", 0); test("kprobe:f { ustack(1) }", 0); test("kprobe:f { cat(\"/proc/uptime\") }", 0); test("uprobe:/bin/bash:main { uaddr(\"glob_asciirange\") }", 0); test("kprobe:f { cgroupid(\"/sys/fs/cgroup/unified/mycg\"); }", 0); test("kprobe:f { macaddr(0xffff) }", 0); } TEST(semantic_analyser, undefined_map) { test("kprobe:f / @mymap == 123 / { @mymap = 0 }", 0); test("kprobe:f / @mymap == 123 / { 456; }", 10); test("kprobe:f / @mymap1 == 1234 / { 1234; @mymap1 = @mymap2; }", 10); } TEST(semantic_analyser, consistent_map_values) { test("kprobe:f { @x = 0; @x = 1; }", 0); test("kprobe:f { @x = 0; @x = \"a\"; }", 1); } TEST(semantic_analyser, consistent_map_keys) { test("kprobe:f { @x = 0; @x; }", 0); test("kprobe:f { @x[1] = 0; @x[2]; }", 0); test("kprobe:f { @x = 0; @x[1]; }", 10); test("kprobe:f { @x[1] = 0; @x; }", 10); test("kprobe:f { @x[1,2] = 0; @x[3,4]; }", 0); test("kprobe:f { @x[1,2] = 0; @x[3]; }", 10); test("kprobe:f { @x[1] = 0; @x[2,3]; }", 10); test("kprobe:f { @x[1,\"a\",kstack] = 0; @x[2,\"b\", kstack]; }", 0); test("kprobe:f { @x[1,\"a\",kstack] = 0; @x[\"b\", 2, kstack]; }", 10); } TEST(semantic_analyser, if_statements) { test("kprobe:f { if(1) { 123 } }", 0); test("kprobe:f { if(1) { 123 } else { 456 } }", 0); test("kprobe:f { if(0) { 123 } else if(1) { 456 } else { 789 } }", 0); test("kprobe:f { if((int32)pid) { 123 } }", 0); } TEST(semantic_analyser, predicate_expressions) { test("kprobe:f / 999 / { 123 }", 0); test("kprobe:f / \"str\" / { 123 }", 10); test("kprobe:f / kstack / { 123 }", 10); test("kprobe:f / @mymap / { @mymap = \"str\" }", 10); } TEST(semantic_analyser, ternary_expressions) { test("kprobe:f { @x = pid < 10000 ? 1 : 2 }", 0); test("kprobe:f { @x = pid < 10000 ? \"lo\" : \"high\" }", 0); test("kprobe:f { pid < 10000 ? printf(\"lo\") : exit() }", 0); test("kprobe:f { @x = pid < 10000 ? printf(\"lo\") : cat(\"/proc/uptime\") }", 10); test("kprobe:f { pid < 10000 ? 3 : cat(\"/proc/uptime\") }", 10); test("kprobe:f { @x = pid < 10000 ? 1 : \"high\" }", 10); test("kprobe:f { @x = pid < 10000 ? \"lo\" : 2 }", 10); } TEST(semantic_analyser, mismatched_call_types) { test("kprobe:f { @x = 1; @x = count(); }", 1); test("kprobe:f { @x = count(); @x = sum(pid); }", 1); test("kprobe:f { @x = 1; @x = hist(0); }", 1); } TEST(semantic_analyser, compound_left) { test("kprobe:f { $a <<= 0 }", 1); test("kprobe:f { $a = 0; $a <<= 1 }", 0); test("kprobe:f { @a <<= 1 }", 0); } TEST(semantic_analyser, compound_right) { test("kprobe:f { $a >>= 0 }", 1); test("kprobe:f { $a = 0; $a >>= 1 }", 0); test("kprobe:f { @a >>= 1 }", 0); } TEST(semantic_analyser, compound_plus) { test("kprobe:f { $a += 0 }", 1); test("kprobe:f { $a = 0; $a += 1 }", 0); test("kprobe:f { @a += 1 }", 0); } TEST(semantic_analyser, compound_minus) { test("kprobe:f { $a -= 0 }", 1); test("kprobe:f { $a = 0; $a -= 1 }", 0); test("kprobe:f { @a -= 1 }", 0); } TEST(semantic_analyser, compound_mul) { test("kprobe:f { $a *= 0 }", 1); test("kprobe:f { $a = 0; $a *= 1 }", 0); test("kprobe:f { @a *= 1 }", 0); } TEST(semantic_analyser, compound_div) { test("kprobe:f { $a /= 0 }", 1); test("kprobe:f { $a = 0; $a /= 1 }", 0); test("kprobe:f { @a /= 1 }", 0); } TEST(semantic_analyser, compound_mod) { test("kprobe:f { $a %= 0 }", 1); test("kprobe:f { $a = 0; $a %= 1 }", 0); test("kprobe:f { @a %= 1 }", 0); } TEST(semantic_analyser, compound_band) { test("kprobe:f { $a &= 0 }", 1); test("kprobe:f { $a = 0; $a &= 1 }", 0); test("kprobe:f { @a &= 1 }", 0); } TEST(semantic_analyser, compound_bor) { test("kprobe:f { $a |= 0 }", 1); test("kprobe:f { $a = 0; $a |= 1 }", 0); test("kprobe:f { @a |= 1 }", 0); } TEST(semantic_analyser, compound_bxor) { test("kprobe:f { $a ^= 0 }", 1); test("kprobe:f { $a = 0; $a ^= 1 }", 0); test("kprobe:f { @a ^= 1 }", 0); } TEST(semantic_analyser, call_hist) { test("kprobe:f { @x = hist(1); }", 0); test("kprobe:f { @x = hist(); }", 1); test("kprobe:f { hist(1); }", 1); test("kprobe:f { $x = hist(1); }", 1); test("kprobe:f { @x[hist(1)] = 1; }", 1); test("kprobe:f { if(hist()) { 123 } }", 1); test("kprobe:f { hist() ? 0 : 1; }", 1); } TEST(semantic_analyser, call_lhist) { test("kprobe:f { @ = lhist(5, 0, 10, 1); }", 0); test("kprobe:f { @ = lhist(5, 0, 10); }", 1); test("kprobe:f { @ = lhist(5, 0); }", 1); test("kprobe:f { @ = lhist(5); }", 1); test("kprobe:f { @ = lhist(); }", 1); test("kprobe:f { @ = lhist(5, 0, 10, 1, 2); }", 1); test("kprobe:f { lhist(-10, -10, 10, 1); }", 1); test("kprobe:f { @ = lhist(-10, -10, 10, 1); }", 10); // must be positive test("kprobe:f { $x = lhist(); }", 1); test("kprobe:f { @[lhist()] = 1; }", 1); test("kprobe:f { if(lhist()) { 123 } }", 1); test("kprobe:f { lhist() ? 0 : 1; }", 1); } TEST(semantic_analyser, call_lhist_posparam) { BPFtrace bpftrace; bpftrace.add_param("0"); bpftrace.add_param("10"); bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, "kprobe:f { @ = lhist(5, $1, $2, $3); }", 0); test(bpftrace, "kprobe:f { @ = lhist(5, $1, $2, $4); }", 10); } TEST(semantic_analyser, call_count) { test("kprobe:f { @x = count(); }", 0); test("kprobe:f { @x = count(1); }", 1); test("kprobe:f { count(); }", 1); test("kprobe:f { $x = count(); }", 1); test("kprobe:f { @[count()] = 1; }", 1); test("kprobe:f { if(count()) { 123 } }", 1); test("kprobe:f { count() ? 0 : 1; }", 1); } TEST(semantic_analyser, call_sum) { test("kprobe:f { @x = sum(123); }", 0); test("kprobe:f { @x = sum(); }", 1); test("kprobe:f { @x = sum(123, 456); }", 1); test("kprobe:f { sum(123); }", 1); test("kprobe:f { $x = sum(123); }", 1); test("kprobe:f { @[sum(123)] = 1; }", 1); test("kprobe:f { if(sum(1)) { 123 } }", 1); test("kprobe:f { sum(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_min) { test("kprobe:f { @x = min(123); }", 0); test("kprobe:f { @x = min(); }", 1); test("kprobe:f { min(123); }", 1); test("kprobe:f { $x = min(123); }", 1); test("kprobe:f { @[min(123)] = 1; }", 1); test("kprobe:f { if(min(1)) { 123 } }", 1); test("kprobe:f { min(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_max) { test("kprobe:f { @x = max(123); }", 0); test("kprobe:f { @x = max(); }", 1); test("kprobe:f { max(123); }", 1); test("kprobe:f { $x = max(123); }", 1); test("kprobe:f { @[max(123)] = 1; }", 1); test("kprobe:f { if(max(1)) { 123 } }", 1); test("kprobe:f { max(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_avg) { test("kprobe:f { @x = avg(123); }", 0); test("kprobe:f { @x = avg(); }", 1); test("kprobe:f { avg(123); }", 1); test("kprobe:f { $x = avg(123); }", 1); test("kprobe:f { @[avg(123)] = 1; }", 1); test("kprobe:f { if(avg(1)) { 123 } }", 1); test("kprobe:f { avg(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_stats) { test("kprobe:f { @x = stats(123); }", 0); test("kprobe:f { @x = stats(); }", 1); test("kprobe:f { stats(123); }", 1); test("kprobe:f { $x = stats(123); }", 1); test("kprobe:f { @[stats(123)] = 1; }", 1); test("kprobe:f { if(stats(1)) { 123 } }", 1); test("kprobe:f { stats(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_delete) { test("kprobe:f { @x = 1; delete(@x); }", 0); test("kprobe:f { delete(1); }", 1); test("kprobe:f { delete(); }", 1); test("kprobe:f { @y = delete(@x); }", 1); test("kprobe:f { $y = delete(@x); }", 1); test("kprobe:f { @[delete(@x)] = 1; }", 1); test("kprobe:f { @x = 1; if(delete(@x)) { 123 } }", 10); test("kprobe:f { @x = 1; delete(@x) ? 0 : 1; }", 10); } TEST(semantic_analyser, call_exit) { test("kprobe:f { exit(); }", 0); test("kprobe:f { exit(1); }", 1); test("kprobe:f { @a = exit(); }", 1); test("kprobe:f { @a = exit(1); }", 1); test("kprobe:f { $a = exit(1); }", 1); test("kprobe:f { @[exit(1)] = 1; }", 1); test("kprobe:f { if(exit()) { 123 } }", 10); test("kprobe:f { exit() ? 0 : 1; }", 10); } TEST(semantic_analyser, call_print) { test("kprobe:f { @x = count(); print(@x); }", 0); test("kprobe:f { @x = count(); print(@x, 5); }", 0); test("kprobe:f { @x = count(); print(@x, 5, 10); }", 0); test("kprobe:f { @x = count(); print(@x, 5, 10, 1); }", 1); test("kprobe:f { @x = count(); @x = print(); }", 1); test("kprobe:f { print(@x); @x[1,2] = count(); }", 0); test("kprobe:f { @x[1,2] = count(); print(@x); }", 0); test("kprobe:f { @x[1,2] = count(); print(@x[3,4]); }", 1); test("kprobe:f { @x = count(); @ = print(@x); }", 1); test("kprobe:f { @x = count(); $y = print(@x); }", 1); test("kprobe:f { @x = count(); @[print(@x)] = 1; }", 1); test("kprobe:f { @x = count(); if(print(@x)) { 123 } }", 10); test("kprobe:f { @x = count(); print(@x) ? 0 : 1; }", 10); test_for_warning("kprobe:f { @x = stats(10); print(@x, 2); }", "top and div arguments are ignored"); test_for_warning("kprobe:f { @x = stats(10); print(@x, 2, 3); }", "top and div arguments are ignored"); } TEST(semantic_analyser, call_print_non_map) { test(R"_(BEGIN { print(1) })_", 0); test(R"_(BEGIN { print(comm) })_", 0); test(R"_(BEGIN { print(nsecs) })_", 0); test(R"_(BEGIN { print("string") })_", 0); test(R"_(BEGIN { print((1, 2, "tuple")) })_", 0); test(R"_(BEGIN { $x = 1; print($x) })_", 0); test(R"_(BEGIN { $x = 1; $y = $x + 3; print($y) })_", 0); test(R"_(BEGIN { print(3, 5) })_", 1); test(R"_(BEGIN { print(3, 5, 2) })_", 1); test(R"_(BEGIN { print(exit()) })_", 10); test(R"_(BEGIN { print(count()) })_", 1); test(R"_(BEGIN { print(ctx) })_", 10); test(R"_(BEGIN { print((int8 *)0) })_", 10); } TEST(semantic_analyser, call_clear) { test("kprobe:f { @x = count(); clear(@x); }", 0); test("kprobe:f { @x = count(); clear(@x, 1); }", 1); test("kprobe:f { @x = count(); @x = clear(); }", 1); test("kprobe:f { clear(@x); @x[1,2] = count(); }", 0); test("kprobe:f { @x[1,2] = count(); clear(@x); }", 0); test("kprobe:f { @x[1,2] = count(); clear(@x[3,4]); }", 1); test("kprobe:f { @x = count(); @ = clear(@x); }", 1); test("kprobe:f { @x = count(); $y = clear(@x); }", 1); test("kprobe:f { @x = count(); @[clear(@x)] = 1; }", 1); test("kprobe:f { @x = count(); if(clear(@x)) { 123 } }", 10); test("kprobe:f { @x = count(); clear(@x) ? 0 : 1; }", 10); } TEST(semantic_analyser, call_zero) { test("kprobe:f { @x = count(); zero(@x); }", 0); test("kprobe:f { @x = count(); zero(@x, 1); }", 1); test("kprobe:f { @x = count(); @x = zero(); }", 1); test("kprobe:f { zero(@x); @x[1,2] = count(); }", 0); test("kprobe:f { @x[1,2] = count(); zero(@x); }", 0); test("kprobe:f { @x[1,2] = count(); zero(@x[3,4]); }", 1); test("kprobe:f { @x = count(); @ = zero(@x); }", 1); test("kprobe:f { @x = count(); $y = zero(@x); }", 1); test("kprobe:f { @x = count(); @[zero(@x)] = 1; }", 1); test("kprobe:f { @x = count(); if(zero(@x)) { 123 } }", 10); test("kprobe:f { @x = count(); zero(@x) ? 0 : 1; }", 10); } TEST(semantic_analyser, call_time) { test("kprobe:f { time(); }", 0); test("kprobe:f { time(\"%M:%S\"); }", 0); test("kprobe:f { time(\"%M:%S\", 1); }", 1); test("kprobe:f { @x = time(); }", 1); test("kprobe:f { $x = time(); }", 1); test("kprobe:f { @[time()] = 1; }", 1); test("kprobe:f { time(1); }", 10); test("kprobe:f { $x = \"str\"; time($x); }", 10); test("kprobe:f { if(time()) { 123 } }", 10); test("kprobe:f { time() ? 0 : 1; }", 10); } TEST(semantic_analyser, call_strftime) { test("kprobe:f { strftime(\"%M:%S\", 1); }", 0); test("kprobe:f { strftime(\"%M:%S\", nsecs); }", 0); test("kprobe:f { strftime(\"%M:%S\", \"\"); }", 10); test("kprobe:f { strftime(1, nsecs); }", 10); test("kprobe:f { $var = \"str\"; strftime($var, nsecs); }", 10); test("kprobe:f { strftime(); }", 1); test("kprobe:f { strftime(\"%M:%S\"); }", 1); test("kprobe:f { strftime(\"%M:%S\", 1, 1); }", 1); test("kprobe:f { strftime(1, 1, 1); }", 1); test("kprobe:f { strftime(\"%M:%S\", \"\", 1); }", 1); test("kprobe:f { $ts = strftime(\"%M:%S\", 1); }", 0); test("kprobe:f { @ts = strftime(\"%M:%S\", nsecs); }", 0); test("kprobe:f { @[strftime(\"%M:%S\", nsecs)] = 1; }", 0); test("kprobe:f { printf(\"%s\", strftime(\"%M:%S\", nsecs)); }", 0); test("kprobe:f { strncmp(\"str\", strftime(\"%M:%S\", nsecs), 10); }", 10); } TEST(semantic_analyser, call_str) { test("kprobe:f { str(arg0); }", 0); test("kprobe:f { @x = str(arg0); }", 0); test("kprobe:f { str(); }", 1); test("kprobe:f { str(\"hello\"); }", 1); } TEST(semantic_analyser, call_str_2_lit) { test("kprobe:f { str(arg0, 3); }", 0); test("kprobe:f { str(arg0, -3); }", 10); test("kprobe:f { @x = str(arg0, 3); }", 0); test("kprobe:f { str(arg0, \"hello\"); }", 10); } TEST(semantic_analyser, call_str_2_expr) { test("kprobe:f { str(arg0, arg1); }", 0); test("kprobe:f { @x = str(arg0, arg1); }", 0); } TEST(semantic_analyser, call_str_state_leak_regression_test) { // Previously, the semantic analyser would leak state in the first str() // call. This would make the semantic analyser think it's still processing // a positional parameter in the second str() call causing confusing error // messages. test(R"PROG(kprobe:f { $x = str($1) == "asdf"; $y = str(arg0) })PROG", 0); } TEST(semantic_analyser, call_buf) { test("kprobe:f { buf(arg0, 1); }", 0); test("kprobe:f { buf(arg0, -1); }", 1); test("kprobe:f { @x = buf(arg0, 1); }", 0); test("kprobe:f { $x = buf(arg0, 1); }", 0); test("kprobe:f { buf(); }", 1); test("kprobe:f { buf(\"hello\"); }", 10); test("struct x { int c[4] }; kprobe:f { $foo = (struct x*)0; @x = " "buf($foo->c); }", 0); } TEST(semantic_analyser, call_buf_lit) { test("kprobe:f { @x = buf(arg0, 3); }", 0); test("kprobe:f { buf(arg0, \"hello\"); }", 10); } TEST(semantic_analyser, call_buf_expr) { test("kprobe:f { buf(arg0, arg1); }", 0); test("kprobe:f { @x = buf(arg0, arg1); }", 0); } TEST(semantic_analyser, call_buf_posparam) { BPFtrace bpftrace; bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, "kprobe:f { buf(arg0, $1); }", 0); test(bpftrace, "kprobe:f { buf(arg0, $2); }", 1); } TEST(semantic_analyser, call_ksym) { test("kprobe:f { ksym(arg0); }", 0); test("kprobe:f { @x = ksym(arg0); }", 0); test("kprobe:f { ksym(); }", 1); test("kprobe:f { ksym(\"hello\"); }", 1); } TEST(semantic_analyser, call_usym) { test("kprobe:f { usym(arg0); }", 0); test("kprobe:f { @x = usym(arg0); }", 0); test("kprobe:f { usym(); }", 1); test("kprobe:f { usym(\"hello\"); }", 1); } TEST(semantic_analyser, call_ntop) { std::string structs = "struct inet { unsigned char ipv4[4]; unsigned char ipv6[16]; unsigned char invalid[10]; } "; test("kprobe:f { ntop(2, arg0); }", 0); test("kprobe:f { ntop(arg0); }", 0); test(structs + "kprobe:f { ntop(10, ((struct inet*)0)->ipv4); }", 0); test(structs + "kprobe:f { ntop(10, ((struct inet*)0)->ipv6); }", 0); test(structs + "kprobe:f { ntop(((struct inet*)0)->ipv4); }", 0); test(structs + "kprobe:f { ntop(((struct inet*)0)->ipv6); }", 0); test("kprobe:f { @x = ntop(2, arg0); }", 0); test("kprobe:f { @x = ntop(arg0); }", 0); test("kprobe:f { @x = ntop(2, 0xFFFF); }", 0); test("kprobe:f { @x = ntop(0xFFFF); }", 0); test(structs + "kprobe:f { @x = ntop(((struct inet*)0)->ipv4); }", 0); test(structs + "kprobe:f { @x = ntop(((struct inet*)0)->ipv6); }", 0); // Regression test that ntop can use arguments from the prog context test("tracepoint:tcp:some_tcp_tp { ntop(args->saddr_v6); }", 0); test("kprobe:f { ntop(); }", 1); test("kprobe:f { ntop(2, \"hello\"); }", 1); test("kprobe:f { ntop(\"hello\"); }", 1); test(structs + "kprobe:f { ntop(((struct inet*)0)->invalid); }", 1); } TEST(semantic_analyser, call_kaddr) { test("kprobe:f { kaddr(\"avenrun\"); }", 0); test("kprobe:f { @x = kaddr(\"avenrun\"); }", 0); test("kprobe:f { kaddr(); }", 1); test("kprobe:f { kaddr(123); }", 1); } TEST(semantic_analyser, call_uaddr) { test("u:/bin/bash:main { uaddr(\"github.com/golang/glog.severityName\"); }", 0); test("uprobe:/bin/bash:main { uaddr(\"glob_asciirange\"); }", 0); test("u:/bin/bash:main,u:/bin/bash:readline { uaddr(\"glob_asciirange\"); }", 0); test("uprobe:/bin/bash:main { @x = uaddr(\"glob_asciirange\"); }", 0); test("uprobe:/bin/bash:main { uaddr(); }", 1); test("uprobe:/bin/bash:main { uaddr(123); }", 1); test("uprobe:/bin/bash:main { uaddr(\"?\"); }", 1); test("uprobe:/bin/bash:main { $str = \"glob_asciirange\"; uaddr($str); }", 1); test("uprobe:/bin/bash:main { @str = \"glob_asciirange\"; uaddr(@str); }", 1); test("k:f { uaddr(\"A\"); }", 1); test("i:s:1 { uaddr(\"A\"); }", 1); // The C struct parser should set the is_signed flag on signed types BPFtrace bpftrace; Driver driver(bpftrace); std::string prog = "uprobe:/bin/bash:main {" "$a = uaddr(\"12345_1\");" "$b = uaddr(\"12345_2\");" "$c = uaddr(\"12345_4\");" "$d = uaddr(\"12345_8\");" "$e = uaddr(\"12345_5\");" "$f = uaddr(\"12345_33\");" "}"; test(driver, prog, 0); std::vector sizes = { 8, 16, 32, 64, 64, 64 }; for (size_t i = 0; i < sizes.size(); i++) { auto v = static_cast( driver.root_->probes->at(0)->stmts->at(i)); EXPECT_TRUE(v->var->type.IsPtrTy()); EXPECT_TRUE(v->var->type.GetPointeeTy()->IsIntTy()); EXPECT_EQ((unsigned long int)sizes.at(i), v->var->type.GetPointeeTy()->GetIntBitWidth()); } } TEST(semantic_analyser, call_cgroupid) { // Handle args above STRING_SIZE test("kprobe:f { cgroupid(" // 1 2 3 4 5 6 "\"123456789/123456789/123456789/123456789/123456789/123456789/12345\"" "); }", 0); } TEST(semantic_analyser, call_reg) { #ifdef ARCH_X86_64 test("kprobe:f { reg(\"ip\"); }", 0); test("kprobe:f { @x = reg(\"ip\"); }", 0); #endif test("kprobe:f { reg(\"blah\"); }", 1); test("kprobe:f { reg(); }", 1); test("kprobe:f { reg(123); }", 1); } TEST(semantic_analyser, call_func) { test("kprobe:f { @[func] = count(); }", 0); test("kprobe:f { printf(\"%s\", func); }", 0); test("uprobe:/bin/sh:f { @[func] = count(); }", 0); test("uprobe:/bin/sh:f { printf(\"%s\", func); }", 0); } TEST(semantic_analyser, call_probe) { test("kprobe:f { @[probe] = count(); }", 0); test("kprobe:f { printf(\"%s\", probe); }", 0); } TEST(semantic_analyser, call_cat) { test("kprobe:f { cat(\"/proc/loadavg\"); }", 0); test("kprobe:f { cat(\"/proc/%d/cmdline\", 1); }", 0); test("kprobe:f { cat(); }", 1); test("kprobe:f { cat(123); }", 1); test("kprobe:f { @x = cat(\"/proc/loadavg\"); }", 1); test("kprobe:f { $x = cat(\"/proc/loadavg\"); }", 1); test("kprobe:f { @[cat(\"/proc/loadavg\")] = 1; }", 1); test("kprobe:f { if(cat(\"/proc/loadavg\")) { 123 } }", 10); test("kprobe:f { cat(\"/proc/loadavg\") ? 0 : 1; }", 10); } TEST(semantic_analyser, call_stack) { test("kprobe:f { kstack() }", 0); test("kprobe:f { ustack() }", 0); test("kprobe:f { kstack(bpftrace) }", 0); test("kprobe:f { ustack(bpftrace) }", 0); test("kprobe:f { kstack(perf) }", 0); test("kprobe:f { ustack(perf) }", 0); test("kprobe:f { kstack(3) }", 0); test("kprobe:f { ustack(3) }", 0); test("kprobe:f { kstack(perf, 3) }", 0); test("kprobe:f { ustack(perf, 3) }", 0); // Wrong arguments test("kprobe:f { kstack(3, perf) }", 1); test("kprobe:f { ustack(3, perf) }", 1); test("kprobe:f { kstack(perf, 3, 4) }", 1); test("kprobe:f { ustack(perf, 3, 4) }", 1); test("kprobe:f { kstack(bob) }", 1); test("kprobe:f { ustack(bob) }", 1); test("kprobe:f { kstack(\"str\") }", 1); test("kprobe:f { ustack(\"str\") }", 1); test("kprobe:f { kstack(perf, \"str\") }", 1); test("kprobe:f { ustack(perf, \"str\") }", 1); test("kprobe:f { kstack(\"str\", 3) }", 1); test("kprobe:f { ustack(\"str\", 3) }", 1); // Non-literals test("kprobe:f { @x = perf; kstack(@x) }", 1); test("kprobe:f { @x = perf; ustack(@x) }", 1); test("kprobe:f { @x = perf; kstack(@x, 3) }", 1); test("kprobe:f { @x = perf; ustack(@x, 3) }", 1); test("kprobe:f { @x = 3; kstack(@x) }", 1); test("kprobe:f { @x = 3; ustack(@x) }", 1); test("kprobe:f { @x = 3; kstack(perf, @x) }", 1); test("kprobe:f { @x = 3; ustack(perf, @x) }", 1); // Positional params BPFtrace bpftrace; bpftrace.add_param("3"); bpftrace.add_param("hello"); test(bpftrace, "kprobe:f { kstack($1) }", 0); test(bpftrace, "kprobe:f { ustack($1) }", 0); test(bpftrace, "kprobe:f { kstack(perf, $1) }", 0); test(bpftrace, "kprobe:f { ustack(perf, $1) }", 0); test(bpftrace, "kprobe:f { kstack($2) }", 1); test(bpftrace, "kprobe:f { ustack($2) }", 1); test(bpftrace, "kprobe:f { kstack(perf, $2) }", 1); test(bpftrace, "kprobe:f { ustack(perf, $2) }", 1); } TEST(semantic_analyser, call_macaddr) { std::string structs = "struct mac { char addr[6]; }; struct invalid { char addr[7]; }; "; test("kprobe:f { macaddr(arg0); }", 0); test(structs + "kprobe:f { macaddr((struct mac*)arg0); }", 0); test(structs + "kprobe:f { @x[macaddr((struct mac*)arg0)] = 1; }", 0); test(structs + "kprobe:f { @x = macaddr((struct mac*)arg0); }", 0); test(structs + "kprobe:f { printf(\"%s\", macaddr((struct mac*)arg0)); }", 0); test(structs + "kprobe:f { macaddr(((struct invalid*)arg0)->addr); }", 1); test(structs + "kprobe:f { macaddr(*(struct mac*)arg0); }", 1); test("kprobe:f { macaddr(); }", 1); test("kprobe:f { macaddr(\"hello\"); }", 1); } TEST(semantic_analyser, map_reassignment) { test("kprobe:f { @x = 1; @x = 2; }", 0); test("kprobe:f { @x = 1; @x = \"foo\"; }", 1); } TEST(semantic_analyser, variable_reassignment) { test("kprobe:f { $x = 1; $x = 2; }", 0); test("kprobe:f { $x = 1; $x = \"foo\"; }", 1); } TEST(semantic_analyser, map_use_before_assign) { test("kprobe:f { @x = @y; @y = 2; }", 0); } TEST(semantic_analyser, variable_use_before_assign) { test("kprobe:f { @x = $y; $y = 2; }", 1); } TEST(semantic_analyser, maps_are_global) { test("kprobe:f { @x = 1 } kprobe:g { @y = @x }", 0); test("kprobe:f { @x = 1 } kprobe:g { @x = \"abc\" }", 1); } TEST(semantic_analyser, variables_are_local) { test("kprobe:f { $x = 1 } kprobe:g { $x = \"abc\"; }", 0); test("kprobe:f { $x = 1 } kprobe:g { @y = $x }", 1); } TEST(semantic_analyser, array_access) { test("kprobe:f { $s = arg0; @x = $s->y[0];}", 10); test("kprobe:f { $s = 0; @x = $s->y[0];}", 10); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[5];}", 10); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[-1];}", 10); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[\"0\"];}", 10); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; $idx = 0; @x = $s->y[$idx];}", 10); test("kprobe:f { $s = arg0; @x = $s[0]; }", 10); test("struct MyStruct { void *y; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[5];}", 10); BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[0];}", 0); auto assignment = static_cast( driver.root_->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt64(), assignment->map->type); test(driver, "struct MyStruct { int y[4]; } kprobe:f { $s = ((struct MyStruct *) " "arg0)->y; @x = $s[0];}", 0); auto array_var_assignment = static_cast( driver.root_->probes->at(0)->stmts->at(0)); EXPECT_EQ(CreateArray(4, CreateInt32()), array_var_assignment->var->type); test(driver, "struct MyStruct { int y[4]; } kprobe:f { @a[0] = ((struct MyStruct *) " "arg0)->y; @x = @a[0][0];}", 0); auto array_map_assignment = static_cast( driver.root_->probes->at(0)->stmts->at(0)); EXPECT_EQ(CreateArray(4, CreateInt32()), array_map_assignment->map->type); test(driver, "kprobe:f { $s = (int32 *) arg0; $x = $s[0]; }", 0); auto var_assignment = static_cast( driver.root_->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt32(), var_assignment->var->type); // Positional parameter as index bpftrace.add_param("0"); bpftrace.add_param("hello"); test(bpftrace, "struct MyStruct { int y[4]; } " "kprobe:f { $s = ((struct MyStruct *)arg0)->y[$1]; }", 0); test(bpftrace, "struct MyStruct { int y[4]; } " "kprobe:f { $s = ((struct MyStruct *)arg0)->y[$2]; }", 10); } TEST(semantic_analyser, array_in_map) { test("struct MyStruct { int x[2]; int y[4]; } " "kprobe:f { @ = ((struct MyStruct *)arg0)->x; }", 0); test("struct MyStruct { int x[2]; int y[4]; } " "kprobe:f { @a[0] = ((struct MyStruct *)arg0)->x; }", 0); // Mismatched map value types test("struct MyStruct { int x[2]; int y[4]; } " "kprobe:f { " " @a[0] = ((struct MyStruct *)arg0)->x; " " @a[1] = ((struct MyStruct *)arg0)->y; " "}", 1); test("#include \n" "struct MyStruct { uint8_t x[8]; uint32_t y[2]; }" "kprobe:f { " " @a[0] = ((struct MyStruct *)arg0)->x; " " @a[1] = ((struct MyStruct *)arg0)->y; " "}", 1); } TEST(semantic_analyser, array_as_map_key) { test("struct MyStruct { int x[2]; int y[4]; }" "kprobe:f { @x[((struct MyStruct *)arg0)->x] = 0; }", 0); test("struct MyStruct { int x[2]; int y[4]; }" "kprobe:f { @x[((struct MyStruct *)arg0)->x, " " ((struct MyStruct *)arg0)->y] = 0; }", 0); // Mismatched key types test("struct MyStruct { int x[2]; int y[4]; }" "kprobe:f { " " @x[((struct MyStruct *)arg0)->x] = 0; " " @x[((struct MyStruct *)arg0)->y] = 1; " "}", 10); } TEST(semantic_analyser, variable_type) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $x = 1 }", 0); auto st = CreateInt64(); auto assignment = static_cast(driver.root_->probes->at(0)->stmts->at(0)); EXPECT_EQ(st, assignment->var->type); } TEST(semantic_analyser, unroll) { test("kprobe:f { $i = 0; unroll(5) { printf(\"i: %d\\n\", $i); $i = $i + 1; } }", 0); test("kprobe:f { $i = 0; unroll(101) { printf(\"i: %d\\n\", $i); $i = $i + " "1; } }", 1); test("kprobe:f { $i = 0; unroll(0) { printf(\"i: %d\\n\", $i); $i = $i + 1; } }", 1); BPFtrace bpftrace; bpftrace.add_param("10"); bpftrace.add_param("hello"); bpftrace.add_param("101"); test(bpftrace, "kprobe:f { unroll($#) { printf(\"hi\\n\"); } }", 0); test(bpftrace, "kprobe:f { unroll($1) { printf(\"hi\\n\"); } }", 0); test(bpftrace, "kprobe:f { unroll($2) { printf(\"hi\\n\"); } }", 1); test(bpftrace, "kprobe:f { unroll($3) { printf(\"hi\\n\"); } }", 1); } TEST(semantic_analyser, map_integer_sizes) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $x = (int32) -1; @x = $x; }", 0); auto var_assignment = static_cast(driver.root_->probes->at(0)->stmts->at(0)); auto map_assignment = static_cast(driver.root_->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt32(), var_assignment->var->type); EXPECT_EQ(CreateInt64(), map_assignment->map->type); } TEST(semantic_analyser, unop_dereference) { test("kprobe:f { *0; }", 0); test("struct X { int n; } kprobe:f { $x = (struct X*)0; *$x; }", 0); test("struct X { int n; } kprobe:f { $x = *(struct X*)0; *$x; }", 1); test("kprobe:f { *\"0\"; }", 10); } TEST(semantic_analyser, unop_not) { std::string structs = "struct X { int x; };"; test("kprobe:f { ~0; }", 0); test(structs + "kprobe:f { $x = *(struct X*)0; ~$x; }", 10); test(structs + "kprobe:f { $x = (struct X*)0; ~$x; }", 10); test("kprobe:f { ~\"0\"; }", 10); } TEST(semantic_analyser, unop_lnot) { test("kprobe:f { !0; }", 0); test("kprobe:f { !(int32)0; }", 0); test("struct X { int n; } kprobe:f { $x = (struct X*)0; !$x; }", 10); test("struct X { int n; } kprobe:f { $x = *(struct X*)0; !$x; }", 10); test("kprobe:f { !\"0\"; }", 1); } TEST(semantic_analyser, unop_increment_decrement) { test("kprobe:f { $x = 0; $x++; }", 0); test("kprobe:f { $x = 0; $x--; }", 0); test("kprobe:f { $x = 0; ++$x; }", 0); test("kprobe:f { $x = 0; --$x; }", 0); test("kprobe:f { @x++; }", 0); test("kprobe:f { @x--; }", 0); test("kprobe:f { ++@x; }", 0); test("kprobe:f { --@x; }", 0); test("kprobe:f { $x++; }", 1); test("kprobe:f { @x = \"a\"; @x++; }", 10); test("kprobe:f { $x = \"a\"; $x++; }", 10); } TEST(semantic_analyser, printf) { test("kprobe:f { printf(\"hi\") }", 0); test("kprobe:f { printf(1234) }", 1); test("kprobe:f { printf() }", 1); test("kprobe:f { $fmt = \"mystring\"; printf($fmt) }", 1); test("kprobe:f { printf(\"%s\", comm) }", 0); test("kprobe:f { printf(\"%-16s\", comm) }", 0); test("kprobe:f { printf(\"%-10.10s\", comm) }", 0); test("kprobe:f { printf(\"%A\", comm) }", 10); test("kprobe:f { @x = printf(\"hi\") }", 1); test("kprobe:f { $x = printf(\"hi\") }", 1); test("kprobe:f { printf(\"%d %d %d %d %d %d %d %d %d\", 1, 2, 3, 4, 5, 6, 7, " "8, 9); }", 0); test("kprobe:f { printf(\"%dns\", nsecs) }", 0); { // Long format string should be ok std::stringstream prog; prog << "i:ms:100 { printf(\"" << std::string(200, 'a') << " %d\\n\", 1); }"; test(prog.str(), 0); } } TEST(semantic_analyser, system) { test("kprobe:f { system(\"ls\") }", 0, false /* safe_mode */); test("kprobe:f { system(1234) }", 1, false /* safe_mode */); test("kprobe:f { system() }", 1, false /* safe_mode */); test("kprobe:f { $fmt = \"mystring\"; system($fmt) }", 1, false /* safe_mode */); } TEST(semantic_analyser, printf_format_int) { test("kprobe:f { printf(\"int: %d\", 1234) }", 0); test("kprobe:f { printf(\"int: %d\", pid) }", 0); test("kprobe:f { @x = 123; printf(\"int: %d\", @x) }", 0); test("kprobe:f { $x = 123; printf(\"int: %d\", $x) }", 0); test("kprobe:f { printf(\"int: %u\", 1234) }", 0); test("kprobe:f { printf(\"int: %x\", 1234) }", 0); test("kprobe:f { printf(\"int: %X\", 1234) }", 0); } TEST(semantic_analyser, printf_format_int_with_length) { test("kprobe:f { printf(\"int: %d\", 1234) }", 0); test("kprobe:f { printf(\"int: %u\", 1234) }", 0); test("kprobe:f { printf(\"int: %x\", 1234) }", 0); test("kprobe:f { printf(\"int: %X\", 1234) }", 0); test("kprobe:f { printf(\"int: %p\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhd\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhu\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhx\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhX\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhp\", 1234) }", 0); test("kprobe:f { printf(\"int: %hd\", 1234) }", 0); test("kprobe:f { printf(\"int: %hu\", 1234) }", 0); test("kprobe:f { printf(\"int: %hx\", 1234) }", 0); test("kprobe:f { printf(\"int: %hX\", 1234) }", 0); test("kprobe:f { printf(\"int: %hp\", 1234) }", 0); test("kprobe:f { printf(\"int: %ld\", 1234) }", 0); test("kprobe:f { printf(\"int: %lu\", 1234) }", 0); test("kprobe:f { printf(\"int: %lx\", 1234) }", 0); test("kprobe:f { printf(\"int: %lX\", 1234) }", 0); test("kprobe:f { printf(\"int: %lp\", 1234) }", 0); test("kprobe:f { printf(\"int: %lld\", 1234) }", 0); test("kprobe:f { printf(\"int: %llu\", 1234) }", 0); test("kprobe:f { printf(\"int: %llx\", 1234) }", 0); test("kprobe:f { printf(\"int: %llX\", 1234) }", 0); test("kprobe:f { printf(\"int: %llp\", 1234) }", 0); test("kprobe:f { printf(\"int: %jd\", 1234) }", 0); test("kprobe:f { printf(\"int: %ju\", 1234) }", 0); test("kprobe:f { printf(\"int: %jx\", 1234) }", 0); test("kprobe:f { printf(\"int: %jX\", 1234) }", 0); test("kprobe:f { printf(\"int: %jp\", 1234) }", 0); test("kprobe:f { printf(\"int: %zd\", 1234) }", 0); test("kprobe:f { printf(\"int: %zu\", 1234) }", 0); test("kprobe:f { printf(\"int: %zx\", 1234) }", 0); test("kprobe:f { printf(\"int: %zX\", 1234) }", 0); test("kprobe:f { printf(\"int: %zp\", 1234) }", 0); test("kprobe:f { printf(\"int: %td\", 1234) }", 0); test("kprobe:f { printf(\"int: %tu\", 1234) }", 0); test("kprobe:f { printf(\"int: %tx\", 1234) }", 0); test("kprobe:f { printf(\"int: %tX\", 1234) }", 0); test("kprobe:f { printf(\"int: %tp\", 1234) }", 0); } TEST(semantic_analyser, printf_format_string) { test("kprobe:f { printf(\"str: %s\", \"mystr\") }", 0); test("kprobe:f { printf(\"str: %s\", comm) }", 0); test("kprobe:f { printf(\"str: %s\", str(arg0)) }", 0); test("kprobe:f { @x = \"hi\"; printf(\"str: %s\", @x) }", 0); test("kprobe:f { $x = \"hi\"; printf(\"str: %s\", $x) }", 0); } TEST(semantic_analyser, printf_bad_format_string) { test("kprobe:f { printf(\"%d\", \"mystr\") }", 10); test("kprobe:f { printf(\"%d\", str(arg0)) }", 10); test("kprobe:f { printf(\"%s\", 1234) }", 10); test("kprobe:f { printf(\"%s\", arg0) }", 10); } TEST(semantic_analyser, printf_format_buf) { test("kprobe:f { printf(\"%r\", buf(\"mystr\", 5)) }", 0); } TEST(semantic_analyser, printf_bad_format_buf) { test("kprobe:f { printf(\"%r\", \"mystr\") }", 10); test("kprobe:f { printf(\"%r\", arg0) }", 10); } TEST(semantic_analyser, printf_format_multi) { test("kprobe:f { printf(\"%d %d %s\", 1, 2, \"mystr\") }", 0); test("kprobe:f { printf(\"%d %s %d\", 1, 2, \"mystr\") }", 10); } TEST(semantic_analyser, join) { test("kprobe:f { join(arg0) }", 0); test("kprobe:f { printf(\"%s\", join(arg0)) }", 10); test("kprobe:f { join() }", 1); test("kprobe:f { $fmt = \"mystring\"; join($fmt) }", 10); test("kprobe:f { @x = join(arg0) }", 1); test("kprobe:f { $x = join(arg0) }", 1); } TEST(semantic_analyser, join_delimiter) { test("kprobe:f { join(arg0, \",\") }", 0); test("kprobe:f { printf(\"%s\", join(arg0, \",\")) }", 10); test("kprobe:f { $fmt = \"mystring\"; join($fmt, \",\") }", 10); test("kprobe:f { @x = join(arg0, \",\") }", 1); test("kprobe:f { $x = join(arg0, \",\") }", 1); test("kprobe:f { join(arg0, 3) }", 10); } TEST(semantic_analyser, kprobe) { test("kprobe:f { 1 }", 0); test("kretprobe:f { 1 }", 0); } TEST(semantic_analyser, uprobe) { test("uprobe:/bin/sh:f { 1 }", 0); test("u:/bin/sh:f { 1 }", 0); test("uprobe:/bin/sh:0x10 { 1 }", 0); test("u:/bin/sh:0x10 { 1 }", 0); test("uprobe:/bin/sh:f+0x10 { 1 }", 0); test("u:/bin/sh:f+0x10 { 1 }", 0); test("uprobe:sh:f { 1 }", 0); test("uprobe:/notexistfile:f { 1 }", 1); test("uprobe:notexistfile:f { 1 }", 1); test("uretprobe:/bin/sh:f { 1 }", 0); test("ur:/bin/sh:f { 1 }", 0); test("uretprobe:sh:f { 1 }", 0); test("ur:sh:f { 1 }", 0); test("uretprobe:/bin/sh:0x10 { 1 }", 0); test("ur:/bin/sh:0x10 { 1 }", 0); test("uretprobe:/notexistfile:f { 1 }", 1); test("uretprobe:notexistfile:f { 1 }", 1); } TEST(semantic_analyser, usdt) { test("usdt:/bin/sh:probe { 1 }", 0); test("usdt:sh:probe { 1 }", 0); test("usdt:/bin/sh:namespace:probe { 1 }", 0); test("usdt:/notexistfile:probe { 1 }", 1); test("usdt:notexistfile:probe { 1 }", 1); } TEST(semantic_analyser, begin_end_probes) { test("BEGIN { 1 }", 0); test("BEGIN { 1 } BEGIN { 2 }", 10); test("END { 1 }", 0); test("END { 1 } END { 2 }", 10); } TEST(semantic_analyser, tracepoint) { test("tracepoint:category:event { 1 }", 0); } #if defined(ARCH_X86_64) || defined(ARCH_AARCH64) TEST(semantic_analyser, watchpoint_invalid_modes) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); #ifdef ARCH_X86_64 test(*bpftrace, "watchpoint:0x1234:8:r { 1 }", 1); #elif ARCH_AARCH64 test(*bpftrace, "watchpoint:0x1234:8:r { 1 }", 0); #endif test(*bpftrace, "watchpoint:0x1234:8:rx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:wx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:xw { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:rwx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:xx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:b { 1 }", 1); } TEST(semantic_analyser, watchpoint_absolute) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "watchpoint:0x1234:8:rw { 1 }", 0); test(*bpftrace, "watchpoint:0x1234:9:rw { 1 }", 1); test(*bpftrace, "watchpoint:0x0:8:rw { 1 }", 1); } TEST(semantic_analyser, watchpoint_function) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "watchpoint:func1+arg2:8:rw { 1 }", 0); test(*bpftrace, "w:func1+arg2:8:rw { 1 }", 0); test(*bpftrace, "w:func1.one_two+arg2:8:rw { 1 }", 0); test(*bpftrace, "watchpoint:func1+arg99999:8:rw { 1 }", 1); bpftrace->procmon_ = std::make_unique(0); test(*bpftrace, "watchpoint:func1+arg2:8:rw { 1 }", 1); } TEST(semantic_analyser, asyncwatchpoint) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "asyncwatchpoint:func1+arg2:8:rw { 1 }", 0); test(*bpftrace, "aw:func1+arg2:8:rw { 1 }", 0); test(*bpftrace, "aw:func1.one_two+arg2:8:rw { 1 }", 0); test(*bpftrace, "asyncwatchpoint:func1+arg99999:8:rw { 1 }", 1); // asyncwatchpoint's may not use absolute addresses test(*bpftrace, "asyncwatchpoint:0x1234:8:rw { 1 }", 1); bpftrace->procmon_ = std::make_unique(0); test(*bpftrace, "watchpoint:func1+arg2:8:rw { 1 }", 1); } #endif // if defined(ARCH_X86_64) || defined(ARCH_AARCH64) TEST(semantic_analyser, args_builtin_wrong_use) { test("BEGIN { args->foo }", 1); test("END { args->foo }", 1); test("kprobe:f { args->foo }", 1); test("kretprobe:f { args->foo }", 1); test("uretprobe:/bin/sh/:f { args->foo }", 1); test("profile:ms:1 { args->foo }", 1); test("usdt:sh:probe { args->foo }", 1); test("profile:ms:100 { args->foo }", 1); test("hardware:cache-references:1000000 { args->foo }", 1); test("software:faults:1000 { args->foo }", 1); test("interval:s:1 { args->foo }", 1); } TEST(semantic_analyser, profile) { test("profile:hz:997 { 1 }", 0); test("profile:s:10 { 1 }", 0); test("profile:ms:100 { 1 }", 0); test("profile:us:100 { 1 }", 0); test("profile:unit:100 { 1 }", 1); } TEST(semantic_analyser, interval) { test("interval:hz:997 { 1 }", 0); test("interval:s:10 { 1 }", 0); test("interval:ms:100 { 1 }", 0); test("interval:us:100 { 1 }", 0); test("interval:unit:100 { 1 }", 1); } TEST(semantic_analyser, variable_cast_types) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { $x = (struct type1*)cpu; $x = (struct type1*)cpu; }", 0); test(structs + "kprobe:f { $x = (struct type1*)cpu; $x = (struct type2*)cpu; }", 1); } TEST(semantic_analyser, map_cast_types) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { @x = *(struct type1*)cpu; @x = *(struct type1*)cpu; }", 0); test(structs + "kprobe:f { @x = *(struct type1*)cpu; @x = *(struct type2*)cpu; }", 1); } TEST(semantic_analyser, variable_casts_are_local) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { $x = *(struct type1 *)cpu } " "kprobe:g { $x = *(struct type2 *)cpu; }", 0); } TEST(semantic_analyser, map_casts_are_global) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { @x = *(struct type1 *)cpu }" "kprobe:g { @x = *(struct type2 *)cpu }", 1); } TEST(semantic_analyser, cast_unknown_type) { test("kprobe:f { (struct faketype *)cpu }", 1); } TEST(semantic_analyser, cast_struct) { // Casting struct by value is forbidden test("struct type { int field; }" "kprobe:f { $s = (struct type *)cpu; $u = (uint32)*$s; }", 1); test("struct type { int field; } kprobe:f { $s = (struct type)cpu }", 1); } TEST(semantic_analyser, field_access) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { $x = *(struct type1*)cpu; $x.field }", 0); test(structs + "kprobe:f { @x = *(struct type1*)cpu; @x.field }", 0); test("struct task_struct {int x;} kprobe:f { curtask->x }", 0); } TEST(semantic_analyser, field_access_wrong_field) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { ((struct type1 *)cpu)->blah }", 1); test(structs + "kprobe:f { $x = (struct type1 *)cpu; $x->blah }", 1); test(structs + "kprobe:f { @x = (struct type1 *)cpu; @x->blah }", 1); } TEST(semantic_analyser, field_access_wrong_expr) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { 1234->field }", 10); } TEST(semantic_analyser, field_access_types) { std::string structs = "struct type1 { int field; char mystr[8]; }" "struct type2 { int field; }"; test(structs + "kprobe:f { (*((struct type1*)0)).field == 123 }", 0); test(structs + "kprobe:f { (*((struct type1*)0)).field == \"abc\" }", 10); test(structs + "kprobe:f { (*((struct type1*)0)).mystr == \"abc\" }", 0); test(structs + "kprobe:f { (*((struct type1*)0)).mystr == 123 }", 10); test(structs + "kprobe:f { (*((struct type1*)0)).field == (*((struct " "type2*)0)).field }", 0); test(structs + "kprobe:f { (*((struct type1*)0)).mystr == (*((struct " "type2*)0)).field }", 10); } TEST(semantic_analyser, field_access_pointer) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { ((struct type1*)0)->field }", 0); test(structs + "kprobe:f { ((struct type1*)0).field }", 1); test(structs + "kprobe:f { *((struct type1*)0) }", 0); } TEST(semantic_analyser, field_access_sub_struct) { std::string structs = "struct type2 { int field; } " "struct type1 { struct type2 *type2ptr; struct type2 type2; }"; test(structs + "kprobe:f { (*(struct type1*)0).type2ptr->field }", 0); test(structs + "kprobe:f { (*(struct type1*)0).type2.field }", 0); test( structs + "kprobe:f { $x = *(struct type2*)0; $x = (*(struct type1*)0).type2 }", 0); test(structs + "kprobe:f { $x = (struct type2*)0; $x = (*(struct " "type1*)0).type2ptr }", 0); test( structs + "kprobe:f { $x = *(struct type1*)0; $x = (*(struct type1*)0).type2 }", 1); test(structs + "kprobe:f { $x = (struct type1*)0; $x = (*(struct " "type1*)0).type2ptr }", 1); } TEST(semantic_analyser, field_access_is_internal) { BPFtrace bpftrace; Driver driver(bpftrace); std::string structs = "struct type1 { int x; }"; { test(driver, structs + "kprobe:f { $x = (*(struct type1*)0).x }", 0); auto stmts = driver.root_->probes->at(0)->stmts; auto var_assignment1 = static_cast(stmts->at(0)); EXPECT_FALSE(var_assignment1->var->type.is_internal); } { test(driver, structs + "kprobe:f { @type1 = *(struct type1*)0; $x = @type1.x }", 0); auto stmts = driver.root_->probes->at(0)->stmts; auto map_assignment = static_cast(stmts->at(0)); auto var_assignment2 = static_cast(stmts->at(1)); EXPECT_TRUE(map_assignment->map->type.is_internal); EXPECT_TRUE(var_assignment2->var->type.is_internal); } } TEST(semantic_analyser, struct_as_map_key) { test("struct A { int x; } struct B { char x; } " "kprobe:f { @x[*((struct A *)arg0)] = 0; }", 0); test("struct A { int x; } struct B { char x; } " "kprobe:f { @x[*((struct A *)arg0), *((struct B *)arg1)] = 0; }", 0); // Mismatched key types test("struct A { int x; } struct B { char x; } " "kprobe:f { " " @x[*((struct A *)arg0)] = 0; " " @x[*((struct B *)arg1)] = 1; " "}", 10); } TEST(semantic_analyser, probe_short_name) { test("t:a:b { args }", 0); test("k:f { pid }", 0); test("kr:f { pid }", 0); test("u:sh:f { 1 }", 0); test("ur:sh:f { 1 }", 0); test("p:hz:997 { 1 }", 0); test("h:cache-references:1000000 { 1 }", 0); test("s:faults:1000 { 1 }", 0); test("i:s:1 { 1 }", 0); } TEST(semantic_analyser, positional_parameters) { BPFtrace bpftrace; bpftrace.add_param("123"); bpftrace.add_param("hello"); bpftrace.add_param("0x123"); test(bpftrace, "kprobe:f { printf(\"%d\", $1); }", 0); test(bpftrace, "kprobe:f { printf(\"%s\", str($1)); }", 0); test(bpftrace, "kprobe:f { printf(\"%s\", str($2)); }", 0); test(bpftrace, "kprobe:f { printf(\"%s\", str($2 + 1)); }", 0); test(bpftrace, "kprobe:f { printf(\"%d\", $2); }", 10); test(bpftrace, "kprobe:f { printf(\"%d\", $3); }", 0); // Pointer arithmetic in str() for parameters // Only str($1 + CONST) where CONST <= strlen($1) should be allowed test(bpftrace, "kprobe:f { printf(\"%s\", str($1 + 1)); }", 0); test(bpftrace, "kprobe:f { printf(\"%s\", str(1 + $1)); }", 0); test(bpftrace, "kprobe:f { printf(\"%s\", str($1 + 4)); }", 10); test(bpftrace, "kprobe:f { printf(\"%s\", str($1 * 2)); }", 10); test(bpftrace, "kprobe:f { printf(\"%s\", str($1 + 1 + 1)); }", 1); // Parameters are not required to exist to be used: test(bpftrace, "kprobe:f { printf(\"%s\", str($4)); }", 0); test(bpftrace, "kprobe:f { printf(\"%d\", $4); }", 0); test(bpftrace, "kprobe:f { printf(\"%d\", $#); }", 0); test(bpftrace, "kprobe:f { printf(\"%s\", str($#)); }", 1); test(bpftrace, "kprobe:f { printf(\"%s\", str($#+1)); }", 1); // Parameters can be used as string literals test(bpftrace, "kprobe:f { printf(\"%d\", cgroupid(str($2))); }", 0); Driver driver(bpftrace); test(driver, "k:f { $1 }", 0); auto stmt = static_cast( driver.root_->probes->at(0)->stmts->at(0)); auto pp = static_cast(stmt->expr); EXPECT_EQ(CreateInt64(), pp->type); EXPECT_TRUE(pp->is_literal); bpftrace.add_param("0999"); test(bpftrace, "kprobe:f { printf(\"%d\", $4); }", 10); } TEST(semantic_analyser, macros) { test("#define A 1\nkprobe:f { printf(\"%d\", A); }", 0); test("#define A A\nkprobe:f { printf(\"%d\", A); }", 1); test("enum { A = 1 }\n#define A A\nkprobe:f { printf(\"%d\", A); }", 0); } TEST(semantic_analyser, enums) { test("enum { a = 1, b } kprobe:f { printf(\"%d\", a); }", 0); } TEST(semantic_analyser, signed_int_comparison_warnings) { bool invert = true; std::string cmp_sign = "comparison of integers of different signs"; test_for_warning("kretprobe:f /-1 < retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 > retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 >= retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 <= retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 != retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 == retval/ {}", cmp_sign); test_for_warning("kretprobe:f /retval > -1/ {}", cmp_sign); test_for_warning("kretprobe:f /retval < -1/ {}", cmp_sign); // These should not trigger a warning test_for_warning("kretprobe:f /1 < retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 > retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 >= retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 <= retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 != retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 == retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /retval > 1/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /retval < 1/ {}", cmp_sign, invert); } TEST(semantic_analyser, string_comparison) { test("struct MyStruct {char y[4]; } kprobe:f { $s = (struct MyStruct*)arg0; " "$s->y == \"abc\"}", 0); test("struct MyStruct {char y[4]; } kprobe:f { $s = (struct MyStruct*)arg0; " "\"abc\" != $s->y}", 0); test("struct MyStruct {char y[4]; } kprobe:f { $s = (struct MyStruct*)arg0; " "\"abc\" == \"abc\"}", 0); bool invert = true; std::string msg = "the condition is always false"; test_for_warning("struct MyStruct {char y[4]; } kprobe:f { $s = (struct " "MyStruct*)arg0; $s->y == \"long string\"}", msg, invert); test_for_warning("struct MyStruct {char y[4]; } kprobe:f { $s = (struct " "MyStruct*)arg0; \"long string\" != $s->y}", msg, invert); } TEST(semantic_analyser, signed_int_arithmetic_warnings) { // Test type warnings for arithmetic bool invert = true; std::string msg = "arithmetic on integers of different signs"; test_for_warning("kprobe:f { @ = -1 - arg0 }", msg); test_for_warning("kprobe:f { @ = -1 + arg0 }", msg); test_for_warning("kprobe:f { @ = -1 * arg0 }", msg); test_for_warning("kprobe:f { @ = -1 / arg0 }", msg); test_for_warning("kprobe:f { @ = arg0 + 1 }", msg, invert); test_for_warning("kprobe:f { @ = arg0 - 1 }", msg, invert); test_for_warning("kprobe:f { @ = arg0 * 1 }", msg, invert); test_for_warning("kprobe:f { @ = arg0 / 1 }", msg, invert); } TEST(semantic_analyser, signed_int_division_warnings) { bool invert = true; std::string msg = "signed operands"; test_for_warning("kprobe:f { @ = -1 / 1 }", msg); test_for_warning("kprobe:f { @ = 1 / -1 }", msg); // These should not trigger a warning test_for_warning("kprobe:f { @ = 1 / 1 }", msg, invert); test_for_warning("kprobe:f { @ = -(1 / 1) }", msg, invert); } TEST(semantic_analyser, signed_int_modulo_warnings) { bool invert = true; std::string msg = "signed operands"; test_for_warning("kprobe:f { @ = -1 % 1 }", msg); test_for_warning("kprobe:f { @ = 1 % -1 }", msg); // These should not trigger a warning test_for_warning("kprobe:f { @ = 1 % 1 }", msg, invert); test_for_warning("kprobe:f { @ = -(1 % 1) }", msg, invert); } TEST(semantic_analyser, map_as_lookup_table) { // Initializing a map should not lead to usage issues test("BEGIN { @[0] = \"abc\"; @[1] = \"def\" } kretprobe:f { printf(\"%s\\n\", @[retval])}"); } TEST(semantic_analyser, cast_sign) { // The C struct parser should set the is_signed flag on signed types BPFtrace bpftrace; Driver driver(bpftrace); std::string prog = "struct t { int s; unsigned int us; long l; unsigned long ul }; " "kprobe:f { " " $t = ((struct t *)0xFF);" " $s = $t->s; $us = $t->us; $l = $t->l; $lu = $t->ul; }"; test(driver, prog, 0); auto s = static_cast(driver.root_->probes->at(0)->stmts->at(1)); auto us = static_cast(driver.root_->probes->at(0)->stmts->at(2)); auto l = static_cast(driver.root_->probes->at(0)->stmts->at(3)); auto ul = static_cast(driver.root_->probes->at(0)->stmts->at(4)); EXPECT_EQ(CreateInt32(), s->var->type); EXPECT_EQ(CreateUInt32(), us->var->type); EXPECT_EQ(CreateInt64(), l->var->type); EXPECT_EQ(CreateUInt64(), ul->var->type); } TEST(semantic_analyser, binop_sign) { // Make sure types are correct std::string prog_pre = "struct t { long l; unsigned long ul }; " "kprobe:f { " " $t = ((struct t *)0xFF); "; std::string operators[] = { "==", "!=", "<", "<=", ">", ">=", "+", "-", "/", "*"}; for(std::string op : operators) { BPFtrace bpftrace; Driver driver(bpftrace); std::string prog = prog_pre + "$varA = $t->l " + op + " $t->l; " "$varB = $t->ul " + op + " $t->l; " "$varC = $t->ul " + op + " $t->ul;" "}"; test(driver, prog, 0); auto varA = static_cast(driver.root_->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt64(), varA->var->type); auto varB = static_cast(driver.root_->probes->at(0)->stmts->at(2)); EXPECT_EQ(CreateUInt64(), varB->var->type); auto varC = static_cast(driver.root_->probes->at(0)->stmts->at(3)); EXPECT_EQ(CreateUInt64(), varC->var->type); } } TEST(semantic_analyser, int_cast_types) { test("kretprobe:f { @ = (int8)retval }", 0); test("kretprobe:f { @ = (int16)retval }", 0); test("kretprobe:f { @ = (int32)retval }", 0); test("kretprobe:f { @ = (int64)retval }", 0); test("kretprobe:f { @ = (uint8)retval }", 0); test("kretprobe:f { @ = (uint16)retval }", 0); test("kretprobe:f { @ = (uint32)retval }", 0); test("kretprobe:f { @ = (uint64)retval }", 0); test("kretprobe:f { @ = (int2)retval }", 1); test("kretprobe:f { @ = (uint2)retval }", 1); } TEST(semantic_analyser, int_cast_usage) { test("kretprobe:f /(int32) retval < 0 / {}", 0); test("kprobe:f /(int32) arg0 < 0 / {}", 0); test("kprobe:f { @=sum((int32)arg0) }", 0); test("kprobe:f { @=avg((int32)arg0) }", 0); test("kprobe:f { @=avg((int32)arg0) }", 0); test("kprobe:f { @=avg((int32)\"abc\") }", 1); } TEST(semantic_analyser, intptr_cast_types) { test("kretprobe:f { @ = *(int8*)retval }", 0); test("kretprobe:f { @ = *(int16*)retval }", 0); test("kretprobe:f { @ = *(int32*)retval }", 0); test("kretprobe:f { @ = *(int64*)retval }", 0); test("kretprobe:f { @ = *(uint8*)retval }", 0); test("kretprobe:f { @ = *(uint16*)retval }", 0); test("kretprobe:f { @ = *(uint32*)retval }", 0); test("kretprobe:f { @ = *(uint64*)retval }", 0); test("kretprobe:f { @ = *(int2*)retval }", 1); test("kretprobe:f { @ = *(uint2*)retval }", 1); } TEST(semantic_analyser, intptr_cast_usage) { test("kretprobe:f /(*(int32*) retval) < 0 / {}", 0); test("kprobe:f /(*(int32*) arg0) < 0 / {}", 0); test("kprobe:f { @=sum(*(int32*)arg0) }", 0); test("kprobe:f { @=avg(*(int32*)arg0) }", 0); test("kprobe:f { @=avg(*(int32*)arg0) }", 0); // This is OK (@ = 0x636261) test("kprobe:f { @=avg(*(int32*)\"abc\") }", 0); test("kprobe:f { @=avg(*(int32*)123) }", 0); } TEST(semantic_analyser, signal) { // int literals test("k:f { signal(1); }", 0, false); test("kr:f { signal(1); }", 0, false); test("u:/bin/sh:f { signal(11); }", 0, false); test("ur:/bin/sh:f { signal(11); }", 0, false); test("p:hz:1 { signal(1); }", 0, false); // vars test("k:f { @=1; signal(@); }", 0, false); test("k:f { @=1; signal((int32)arg0); }", 0, false); // String test("k:f { signal(\"KILL\"); }", 0, false); test("k:f { signal(\"SIGKILL\"); }", 0, false); // Not allowed for: test("hardware:pcm:1000 { signal(1); }", 1, false); test("software:pcm:1000 { signal(1); }", 1, false); test("BEGIN { signal(1); }", 1, false); test("END { signal(1); }", 1, false); test("i:s:1 { signal(1); }", 1, false); // invalid signals test("k:f { signal(0); }", 1, false); test("k:f { signal(-100); }", 1, false); test("k:f { signal(100); }", 1, false); test("k:f { signal(\"SIGABC\"); }", 1, false); test("k:f { signal(\"ABC\"); }", 1, false); // Missing kernel support MockBPFfeature feature(false); test(feature, "k:f { signal(1) }", 1, false); test(feature, "k:f { signal(\"KILL\"); }", 1, false); // Positional parameter BPFtrace bpftrace; bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, "k:f { signal($1) }", 0, false); test(bpftrace, "k:f { signal($2) }", 1, false); } TEST(semantic_analyser, strncmp) { // Test strncmp builtin test("i:s:1 { $a = \"bar\"; strncmp(\"foo\", $a, 1) }", 0); test("i:s:1 { strncmp(\"foo\", \"bar\", 1) }", 0); test("i:s:1 { strncmp(1) }", 1); test("i:s:1 { strncmp(1,1,1) }", 10); test("i:s:1 { strncmp(\"a\",1,1) }", 10); test("i:s:1 { strncmp(\"a\",\"a\",-1) }", 1); test("i:s:1 { strncmp(\"a\",\"a\",\"foo\") }", 1); } TEST(semantic_analyser, strncmp_posparam) { BPFtrace bpftrace; bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, "i:s:1 { strncmp(\"foo\", \"bar\", $1) }", 0); test(bpftrace, "i:s:1 { strncmp(\"foo\", \"bar\", $2) }", 1); } TEST(semantic_analyser, override) { // literals test("k:f { override(-1); }", 0, false); // variables test("k:f { override(arg0); }", 0, false); // Probe types test("kr:f { override(-1); }", 1, false); test("u:/bin/sh:f { override(-1); }", 1, false); test("t:syscalls:sys_enter_openat { override(-1); }", 1, false); test("i:s:1 { override(-1); }", 1, false); test("p:hz:1 { override(-1); }", 1, false); } TEST(semantic_analyser, unwatch) { test("i:s:1 { unwatch(12345) }", 0); test("i:s:1 { unwatch(0x1234) }", 0); test("i:s:1 { $x = 1; unwatch($x); }", 0); test("i:s:1 { @x = 1; @x++; unwatch(@x); }", 0); test("k:f { unwatch(arg0); }", 0); test("k:f { unwatch((int64)arg0); }", 0); test("k:f { unwatch(*(int64*)arg0); }", 0); test("i:s:1 { unwatch(\"asdf\") }", 10); test("i:s:1 { @x[\"hi\"] = \"world\"; unwatch(@x[\"hi\"]) }", 10); test("i:s:1 { printf(\"%d\", unwatch(2)) }", 10); } TEST(semantic_analyser, struct_member_keywords) { std::string keywords[] = { "arg0", "args", "curtask", "func", "gid" "rand", "uid", "avg", "cat", "exit", "kaddr", "min", "printf", "usym", "kstack", "ustack", "bpftrace", "perf", "uprobe", "kprobe", }; for(auto kw : keywords) { test("struct S{ int " + kw + ";}; k:f { ((struct S*)arg0)->" + kw + "}", 0); test("struct S{ int " + kw + ";}; k:f { (*(struct S*)arg0)." + kw + "}", 0); } } TEST(semantic_analyser, jumps) { test("i:s:1 { return; }", 0); // must be used in loops test("i:s:1 { break; }", 1); test("i:s:1 { continue; }", 1); } TEST(semantic_analyser, while_loop) { test("i:s:1 { $a = 1; while ($a < 10) { $a++ }}", 0); test("i:s:1 { $a = 1; while (1) { if($a > 50) { break } $a++ }}", 0); test("i:s:1 { $a = 1; while ($a < 10) { $a++ }}", 0); test("i:s:1 { $a = 1; while (1) { if($a > 50) { break } $a++ }}", 0); test("i:s:1 { $a = 1; while (1) { if($a > 50) { return } $a++ }}", 0); test(R"PROG( i:s:1 { $a = 1; while ($a < 10) { $a++; $j=0; while ($j < 10) { $j++; } } })PROG", 0); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { break; $a++ }}", "code after a 'break'"); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { continue; $a++ }}", "code after a 'continue'"); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { return; $a++ }}", "code after a 'return'"); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { @=$a++; print(@); }}", "'print()' in a loop"); } TEST(semantic_analyser, builtin_args) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "t:sched:sched_one { args->common_field }", 0); test(*bpftrace, "t:sched:sched_two { args->common_field }", 0); test(*bpftrace, "t:sched:sched_one," "t:sched:sched_two { args->common_field }", 0); test(*bpftrace, "t:sched:sched_* { args->common_field }", 0); test(*bpftrace, "t:sched:sched_one { args->not_a_field }", 1); } TEST(semantic_analyser, type_ctx) { BPFtrace bpftrace; Driver driver(bpftrace); std::string structs = "struct c {char c} struct x { long a; short b[4]; " "struct c c; struct c *d;}"; test(driver, structs + "kprobe:f { $x = (struct x*)ctx; $a = $x->a; $b = $x->b[0]; " "$c = $x->c.c; $d = $x->d->c;}", 0); auto &stmts = driver.root_->probes->at(0)->stmts; // $x = (struct x*)ctx; auto assignment = static_cast(stmts->at(0)); EXPECT_TRUE(assignment->var->type.IsPtrTy()); // $a = $x->a; assignment = static_cast(stmts->at(1)); EXPECT_EQ(CreateInt64(), assignment->var->type); auto fieldaccess = static_cast(assignment->expr); EXPECT_EQ(CreateInt64(), fieldaccess->type); auto unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); auto var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); // $b = $x->b[0]; assignment = static_cast(stmts->at(2)); EXPECT_EQ(CreateInt16(), assignment->var->type); auto arrayaccess = static_cast(assignment->expr); EXPECT_EQ(CreateInt16(), arrayaccess->type); fieldaccess = static_cast(arrayaccess->expr); EXPECT_TRUE(fieldaccess->type.IsCtxAccess()); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); #if ARCH_X86_64 auto chartype = CreateInt8(); #else auto chartype = CreateUInt8(); #endif // $c = $x->c.c; assignment = static_cast(stmts->at(3)); EXPECT_EQ(chartype, assignment->var->type); fieldaccess = static_cast(assignment->expr); EXPECT_EQ(chartype, fieldaccess->type); fieldaccess = static_cast(fieldaccess->expr); EXPECT_TRUE(fieldaccess->type.IsCtxAccess()); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); // $d = $x->d->c; assignment = static_cast(stmts->at(4)); EXPECT_EQ(chartype, assignment->var->type); fieldaccess = static_cast(assignment->expr); EXPECT_EQ(chartype, fieldaccess->type); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsRecordTy()); fieldaccess = static_cast(unop->expr); EXPECT_TRUE(fieldaccess->type.IsPtrTy()); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); test(driver, "k:f, kr:f { @ = (uint64)ctx; }", 0); test(driver, "k:f, i:s:1 { @ = (uint64)ctx; }", 1); test(driver, "t:sched:sched_one { @ = (uint64)ctx; }", 1); } TEST(semantic_analyser, double_pointer_basic) { test(R"_(BEGIN { $pp = (int8 **)0; $p = *$pp; $val = *$p; })_", 0); test(R"_(BEGIN { $pp = (int8 **)0; $val = **$pp; })_", 0); const std::string structs = "struct Foo { int x; }"; test(structs + R"_(BEGIN { $pp = (struct Foo **)0; $val = (*$pp)->x; })_", 0); } TEST(semantic_analyser, double_pointer_int) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $pp = (int8 **)1; $p = *$pp; $val = *$p; }", 0); auto &stmts = driver.root_->probes->at(0)->stmts; // $pp = (int8 **)1; auto assignment = static_cast(stmts->at(0)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->GetPointeeTy()->IsIntTy()); EXPECT_EQ( assignment->var->type.GetPointeeTy()->GetPointeeTy()->GetIntBitWidth(), 8ULL); // $p = *$pp; assignment = static_cast(stmts->at(1)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsIntTy()); EXPECT_EQ(assignment->var->type.GetPointeeTy()->GetIntBitWidth(), 8ULL); // $val = *$p; assignment = static_cast(stmts->at(2)); ASSERT_TRUE(assignment->var->type.IsIntTy()); EXPECT_EQ(assignment->var->type.GetIntBitWidth(), 8ULL); } TEST(semantic_analyser, double_pointer_struct) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "struct Foo { char x; long y; }" "kprobe:f { $pp = (struct Foo **)1; $p = *$pp; $val = $p->x; }", 0); auto &stmts = driver.root_->probes->at(0)->stmts; // $pp = (struct Foo **)1; auto assignment = static_cast(stmts->at(0)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsPtrTy()); ASSERT_TRUE( assignment->var->type.GetPointeeTy()->GetPointeeTy()->IsRecordTy()); EXPECT_EQ(assignment->var->type.GetPointeeTy()->GetPointeeTy()->GetName(), "struct Foo"); // $p = *$pp; assignment = static_cast(stmts->at(1)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsRecordTy()); EXPECT_EQ(assignment->var->type.GetPointeeTy()->GetName(), "struct Foo"); // $val = $p->x; assignment = static_cast(stmts->at(2)); ASSERT_TRUE(assignment->var->type.IsIntTy()); EXPECT_EQ(assignment->var->type.GetIntBitWidth(), 8ULL); } TEST(semantic_analyser, pointer_arith) { test(R"_(BEGIN { $t = (int32*) 32; $t = $t + 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t +=1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t++ })_", 0); test(R"_(BEGIN { $t = (int32*) 32; ++$t })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t = $t - 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t -=1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t-- })_", 0); test(R"_(BEGIN { $t = (int32*) 32; --$t })_", 0); // pointer compare test(R"_(BEGIN { $t = (int32*) 32; @ = ($t > $t); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t < $t); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t >= $t); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t <= $t); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t == $t); })_", 0); // map test(R"_(BEGIN { @ = (int32*) 32; @ = @ + 1 })_", 0); test(R"_(BEGIN { @ = (int32*) 32; @ +=1 })_", 0); test(R"_(BEGIN { @ = (int32*) 32; @++ })_", 0); test(R"_(BEGIN { @ = (int32*) 32; ++@ })_", 0); test(R"_(BEGIN { @ = (int32*) 32; @ = @ - 1 })_", 0); test(R"_(BEGIN { @ = (int32*) 32; @ -=1 })_", 0); test(R"_(BEGIN { @ = (int32*) 32; @-- })_", 0); test(R"_(BEGIN { @ = (int32*) 32; --@ })_", 0); // associativity test(R"_(BEGIN { $t = (int32*) 32; $t = $t + 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t = 1 + $t })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t = $t - 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $t = 1 - $t })_", 1); // invalid ops test(R"_(BEGIN { $t = (int32*) 32; $t *= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t /= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t %= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t <<= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t >>= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t -= $t })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t += $t })_", 1); // invalid types test(R"_(BEGIN { $t = (int32*) 32; $t += "abc" })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t += comm })_", 1); test( R"_(struct A {}; BEGIN { $t = (int32*) 32; $s = *(struct A*) 0; $t += $s })_", 1); } TEST(semantic_analyser, pointer_compare) { test(R"_(BEGIN { $t = (int32*) 32; $c = $t < 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t > 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t <= 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t >= 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t != 1 })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t < $t })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t > $t })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t <= $t })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t >= $t })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $c = $t != $t })_", 0); // pointer compare diff types test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t > $y); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t < $y); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t >= $y); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t <= $y); })_", 0); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t == $y); })_", 0); test_for_warning("k:f { $a = (int8*) 1; $b = (int16*) 2; $c = ($a == $b) }", "comparison of distinct pointer types ('int8, 'int16')"); } // Basic functionality test TEST(semantic_analyser, tuple) { test(R"_(BEGIN { $t = (1)})_", 0); test(R"_(BEGIN { $t = (1, 2); $v = $t;})_", 0); test(R"_(BEGIN { $t = (1, 2, "string")})_", 0); test(R"_(BEGIN { $t = (1, 2, "string"); $t = (3, 4, "other"); })_", 0); test(R"_(BEGIN { $t = (1, kstack()) })_", 0); test(R"_(BEGIN { $t = (1, (2,3)) })_", 0); test(R"_(BEGIN { @t = (1)})_", 0); test(R"_(BEGIN { @t = (1, 2); @v = @t;})_", 0); test(R"_(BEGIN { @t = (1, 2, "string")})_", 0); test(R"_(BEGIN { @t = (1, 2, "string"); @t = (3, 4, "other"); })_", 0); test(R"_(BEGIN { @t = (1, kstack()) })_", 0); test(R"_(BEGIN { @t = (1, (2,3)) })_", 0); test(R"_(struct task_struct { int x; } BEGIN { $t = (1, curtask); })_", 0); test(R"_(struct task_struct { int x[4]; } BEGIN { $t = (1, curtask->x); })_", 0); test(R"_(BEGIN { $t = (1, 2); $t = (4, "other"); })_", 10); test(R"_(BEGIN { $t = (1, 2); $t = 5; })_", 1); test(R"_(BEGIN { $t = (1, count()) })_", 1); test(R"_(BEGIN { $t = ((int32)1, (int64)2); $t = ((int64)1, (int32)2); })_", 10); test(R"_(BEGIN { @t = (1, 2); @t = (4, "other"); })_", 1); test(R"_(BEGIN { @t = (1, 2); @t = 5; })_", 1); test(R"_(BEGIN { @t = (1, count()) })_", 1); test(R"_(BEGIN { @t = (1, (aaa)0) })_", 1); test(R"_(BEGIN { @t = (1, !(aaa)0) })_", 1); } TEST(semantic_analyser, tuple_indexing) { test(R"_(BEGIN { (1,2).0 })_", 0); test(R"_(BEGIN { (1,2).1 })_", 0); test(R"_(BEGIN { (1,2,3).2 })_", 0); test(R"_(BEGIN { $t = (1,2,3).0 })_", 0); test(R"_(BEGIN { $t = (1,2,3); $v = $t.0; })_", 0); test(R"_(BEGIN { (1,2,3).3 })_", 10); test(R"_(BEGIN { (1,2,3).9999999999999 })_", 10); } // More in depth inspection of AST TEST(semantic_analyser, tuple_assign_var) { BPFtrace bpftrace; Driver driver(bpftrace); SizedType ty = CreateTuple( bpftrace.structs.AddTuple({ CreateInt64(), CreateString(64) })); test(bpftrace, true, driver, R"_(BEGIN { $t = (1, "str"); $t = (4, "other"); })_", 0); auto &stmts = driver.root_->probes->at(0)->stmts; // $t = (1, "str"); auto assignment = static_cast(stmts->at(0)); EXPECT_EQ(ty, assignment->var->type); // $t = (4, "other"); assignment = static_cast(stmts->at(1)); EXPECT_EQ(ty, assignment->var->type); } // More in depth inspection of AST TEST(semantic_analyser, tuple_assign_map) { BPFtrace bpftrace; Driver driver(bpftrace); SizedType ty; test(bpftrace, true, driver, R"_(BEGIN { @ = (1, 3, 3, 7); @ = (0, 0, 0, 0); })_", 0); auto &stmts = driver.root_->probes->at(0)->stmts; // $t = (1, 3, 3, 7); auto assignment = static_cast(stmts->at(0)); ty = CreateTuple(bpftrace.structs.AddTuple( { CreateInt64(), CreateInt64(), CreateInt64(), CreateInt64() })); EXPECT_EQ(ty, assignment->map->type); // $t = (0, 0, 0, 0); assignment = static_cast(stmts->at(1)); ty = CreateTuple(bpftrace.structs.AddTuple( { CreateInt64(), CreateInt64(), CreateInt64(), CreateInt64() })); EXPECT_EQ(ty, assignment->map->type); } // More in depth inspection of AST TEST(semantic_analyser, tuple_nested) { BPFtrace bpftrace; Driver driver(bpftrace); SizedType ty_inner = CreateTuple( bpftrace.structs.AddTuple({ CreateInt64(), CreateInt64() })); SizedType ty = CreateTuple( bpftrace.structs.AddTuple({ CreateInt64(), ty_inner })); test(bpftrace, true, driver, R"_(BEGIN { $t = (1,(1,2)); })_", 0); auto &stmts = driver.root_->probes->at(0)->stmts; // $t = (1, "str"); auto assignment = static_cast(stmts->at(0)); EXPECT_EQ(ty, assignment->var->type); } TEST(semantic_analyser, tuple_types_unique) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, R"_(BEGIN { $t = (1, "str"); $t = (4, "other"); })_", 0); EXPECT_EQ(bpftrace->structs.GetTuplesCnt(), 1ul); } TEST(semantic_analyser, multi_pass_type_inference_zero_size_int) { auto bpftrace = get_mock_bpftrace(); // The first pass on processing the Unop does not have enough information // to figure out size of `@i` yet. The analyzer figures out the size // after seeing the `@i++`. On the second pass the correct size is // determined. test(*bpftrace, "BEGIN { if (!@i) { @i++; } }", 0); } TEST(semantic_analyser, call_kptr_uptr) { test("k:f { @ = kptr((int8*) arg0); }", 0); test("k:f { $a = kptr((int8*) arg0); }", 0); test("k:f { @ = kptr(arg0); }", 0); test("k:f { $a = kptr(arg0); }", 0); test("k:f { @ = uptr((int8*) arg0); }", 0); test("k:f { $a = uptr((int8*) arg0); }", 0); test("k:f { @ = uptr(arg0); }", 0); test("k:f { $a = uptr(arg0); }", 0); } TEST(semantic_analyser, call_path) { test("kprobe:f { $k = path( arg0 ) }", 1); test("kretprobe:f { $k = path( arg0 ) }", 1); test("tracepoint:category:event { $k = path( NULL ) }", 1); test("kprobe:f { $k = path( arg0 ) }", 1); test("kretprobe:f{ $k = path( \"abc\" ) }", 1); test("tracepoint:category:event { $k = path( -100 ) }", 1); test("uprobe:/bin/bash:f { $k = path( arg0 ) }", 1); test("BEGIN { $k = path( 1 ) }", 1); test("END { $k = path( 1 ) }", 1); } TEST(semantic_analyser, int_ident) { test("BEGIN { sizeof(int32) }", 0); test("BEGIN { print(int32) }", 1); } TEST(semantic_analyser, tracepoint_common_field) { test("tracepoint:file:filename { args->filename }", 0); test("tracepoint:file:filename { args->common_field }", 1); } #ifdef HAVE_LIBBPF_BTF_DUMP #include "btf_common.h" class semantic_analyser_btf : public test_btf { }; #ifdef HAVE_BCC_KFUNC TEST_F(semantic_analyser_btf, kfunc) { test("kfunc:func_1 { 1 }", 0); test("kretfunc:func_1 { 1 }", 0); test("kfunc:func_1 { $x = args->a; $y = args->foo1; $z = args->foo2->f.a; }", 0); test("kretfunc:func_1 { $x = retval; }", 0); test("kretfunc:func_1 { $x = args->foo; }", 1); test("kretfunc:func_1 { $x = args; }", 1); // func_1 and func_2 have different args, but none of them // is used in probe code, so we're good -> PASS test("kfunc:func_1, kfunc:func_2 { }", 0); // func_1 and func_2 have different args, one of them // is used in probe code, we can't continue -> FAIL test("kfunc:func_1, kfunc:func_2 { $x = args->foo; }", 1, true, false, 1); // func_2 and func_3 have same args -> PASS test("kfunc:func_2, kfunc:func_3 { }", 0); // func_2 and func_3 have same args -> PASS test("kfunc:func_2, kfunc:func_3 { $x = args->foo1; }", 0); // aaa does not exist -> PASS semantic analyser, FAIL field analyser test("kfunc:func_2, kfunc:aaa { $x = args->foo1; }", 0, true, false, 1); // func_* have different args, but none of them // is used in probe code, so we're good -> PASS test("kfunc:func_* { }", 0); // func_* have different args, one of them // is used in probe code, we can't continue -> FAIL test("kfunc:func_* { $x = args->foo1; }", 0, true, false, 1); // reg() is not available in kfunc #ifdef ARCH_X86_64 test("kfunc:func_1 { reg(\"ip\") }", 1); test("kretfunc:func_1 { reg(\"ip\") }", 1); #endif } TEST_F(semantic_analyser_btf, short_name) { test("f:func_1 { 1 }", 0); test("fr:func_1 { 1 }", 0); } TEST_F(semantic_analyser_btf, call_path) { test("kfunc:func_1 { $k = path( args->foo1 ) }", 0); test("kretfunc:func_1 { $k = path( retval->foo1 ) }", 0); } #endif // HAVE_BCC_KFUNC TEST_F(semantic_analyser_btf, iter) { test("iter:task { 1 }", 0); test("iter:task_file { 1 }", 0); test("iter:task { $x = ctx->task->pid }", 0); test("iter:task_file { $x = ctx->file->ino }", 0); test("iter:task { $x = args->foo; }", 1); test("iter:task_file { $x = args->foo; }", 1); test("iter:task* { }", 1, true, false, 1, 1); test("iter:task { printf(\"%d\", ctx->task->pid); }", 0); test("iter:task_file { printf(\"%d\", ctx->file->ino); }", 0); test("iter:task,iter:task_file { 1 }", 1); test("iter:task,f:func_1 { 1 }", 1); } #endif // HAVE_LIBBPF_BTF_DUMP #ifdef HAVE_LIBDW #include "dwarf_common.h" class semantic_analyser_dwarf : public test_dwarf { }; TEST_F(semantic_analyser_dwarf, uprobe_args) { std::string uprobe = "uprobe:" + std::string(bin_); test(uprobe + ":func_1 { $x = args->a; }", 0); test(uprobe + ":func_2 { $x = args->b; }", 0); // func_1 and func_2 have different args, but none of them // is used in probe code, so we're good -> PASS test(uprobe + ":func_1, " + uprobe + ":func_2 { }", 0); // func_1 and func_2 have different args, one of them // is used in probe code, we can't continue -> FAIL in the field analyser test(uprobe + ":func_1, " + uprobe + ":func_2 { $x = args->a; }", 0, true, false, 1); // func_2 and func_3 have same args -> PASS test(uprobe + ":func_2, " + uprobe + ":func_3 { }", 0); test(uprobe + ":func_2, " + uprobe + ":func_3 { $x = args->a; }", 0); // "none" is not an arg -> FAIL in semantic analyser test(uprobe + ":func_1 { $x = args->none; }", 1, true, false, 0); // Probes with wildcards (need non-mock BPFtrace) BPFtrace bpftrace; Driver driver(bpftrace); // func_* have different args, but none of them // is used in probe code, so we're good -> PASS test(bpftrace, false, driver, uprobe + ":func_* { }", 0); // func_* have different args, one of them // is used in probe code, we can't continue -> FAIL in the field analyser test(bpftrace, false, driver, uprobe + ":func_* { $x = args->a; }", 0, true, false, 1); } #endif // HAVE_LIBDW } // namespace semantic_analyser } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/testlibs/000077500000000000000000000000001413460502400163545ustar00rootroot00000000000000bpftrace-0.14.0/tests/testlibs/simple.c000066400000000000000000000000721413460502400200100ustar00rootroot00000000000000__attribute__((__visibility__("default"))) void fun() { } bpftrace-0.14.0/tests/testlibs/usdt_tp.c000066400000000000000000000007531413460502400202070ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include "usdt_tp.h" #include #include #include long myclock() { struct timeval tv; gettimeofday(&tv, NULL); DTRACE_PROBE2(tracetestlib, lib_probe_1, tv.tv_sec, "Hello world"); DTRACE_PROBE2(tracetestlib, lib_probe_1, tv.tv_sec, "Hello world2"); DTRACE_PROBE2(tracetestlib2, lib_probe_2, tv.tv_sec, "Hello world3"); return tv.tv_sec; } bpftrace-0.14.0/tests/testlibs/usdt_tp.h000066400000000000000000000001101413460502400201770ustar00rootroot00000000000000#ifndef _USDTLIBX_H_ #define _USDTLIBX_H_ extern long myclock(); #endif bpftrace-0.14.0/tests/testprogs/000077500000000000000000000000001413460502400165555ustar00rootroot00000000000000bpftrace-0.14.0/tests/testprogs/array_access.c000066400000000000000000000007671413460502400213720ustar00rootroot00000000000000struct A { int x[4]; }; struct B { int y[2][2]; }; void test_array(int *a __attribute__((unused))) { } void test_struct(struct A *a __attribute__((unused)), struct B *b __attribute__((unused))) { } int main(int argc __attribute__((unused)), char ** argv __attribute__((unused))) { struct A a; a.x[0] = 1; a.x[1] = 2; a.x[2] = 3; a.x[3] = 4; struct B b; b.y[0][0] = 5; b.y[0][1] = 6; b.y[1][0] = 7; b.y[1][1] = 8; test_struct(&a, &b); test_array(a.x); } bpftrace-0.14.0/tests/testprogs/bitfield_test.c000066400000000000000000000012451413460502400215440ustar00rootroot00000000000000struct Foo { unsigned int a:4, b:8, c:3, d:1, e:16; }; struct Bar { unsigned short a : 4, b : 8, c : 3, d : 1; unsigned int e : 9, f : 15, g : 1, h : 2, i : 5; }; __attribute__((noinline)) unsigned int func(struct Foo *foo) { return foo->b; } __attribute__((noinline)) short func2(struct Bar *bar) { return bar->b; } int main() { struct Foo foo; struct Bar bar; foo.a = 1; foo.b = 2; foo.c = 5; foo.d = 0; foo.e = 65535; func(&foo); bar.a = 1; bar.b = 217; bar.c = 5; bar.d = 1; bar.e = 500; bar.f = 31117; bar.g = 1; bar.h = 2; bar.i = 27; func2(&bar); return 0; } bpftrace-0.14.0/tests/testprogs/complex_struct.c000066400000000000000000000010561413460502400217760ustar00rootroot00000000000000#include #include #include struct Foo { char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4]; }; void func(struct Foo* foo) { (void)foo; } int main() { struct Foo foo = { .a = malloc(4), .b = { 5, 4, 3, 2 }, .c = { 1, 2, 3, 4 }, .d = { 5, 6, 7, 8 }, .e = { 9, 10, 11, 12 }, .f = { 13, 14, 15, 16 } }; strcpy(foo.a, "\x09\x08\x07\x06"); func(&foo); free(foo.a); return 0; } bpftrace-0.14.0/tests/testprogs/intptrcast.c000066400000000000000000000005601413460502400211150ustar00rootroot00000000000000int fn(short p1, short p2, short p3, short p4, short p5, unsigned long p6, short p7, short p8, short p9) { return p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9; } int main() { fn(0x123, 0x456, 0x789, 0xabc, 0xdef, 0x1234567887654321, 0xcba, 0xcba, 0xcba); return 0; } bpftrace-0.14.0/tests/testprogs/mountns_wrapper.c000066400000000000000000000045771413460502400222010ustar00rootroot00000000000000#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #define errExit(msg) \ do \ { \ perror(msg); \ exit(EXIT_FAILURE); \ } while (0) /* Run another simple test program in a different mount namespace. usage: mountns_wrapper some_testprog The test program directory is bind-mounted into a path that is private to its mount namespace. bpftrace will run from the caller's mount namespace, before the unshare. This will cause bpftrace to not be able to see the path within its own mount namespace. To access the path, bpftrace must use /proc/PID/root, to see the mount namespace from the target PID's perspective. This is useful for both uprobe and USDT tests, to ensure that bpftrace can target processes running in containers, such as docker. LIMITATIONS: doesn't pass arguments to test program, as this hasn't been necessary yet. */ int main(int argc, char *argv[]) { const char *private_mount = "/tmp/bpftrace-unshare-mountns-test"; char dpath[PATH_MAX]; char exe[PATH_MAX]; if (argc != 2) errExit("Must specify test program as only argument."); // Enter a new mount namespace if (unshare(CLONE_NEWNS) != 0) errExit("Failed to unshare"); // Recursively set the mount namespace to private, so caller can't see if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) errExit("Failed to make mount private"); // make a tempdir and bind mount containing testprog folder to it if (mkdir(private_mount, 0770) != 0 && (errno != EEXIST)) errExit("Failed to make private mount dir"); int idx = readlink("/proc/self/exe", dpath, sizeof(dpath) - 1); dpath[idx] = '\0'; char *dname = dirname(dpath); if (mount(dname, private_mount, NULL, MS_BIND, NULL) != 0) errExit("Failed to set up private bind mount"); snprintf(exe, PATH_MAX, "%s/%s", private_mount, argv[1]); char *args[] = { exe, NULL }; return execvp(args[0], args); } bpftrace-0.14.0/tests/testprogs/ptr_to_ptr.c000066400000000000000000000004341413460502400211160ustar00rootroot00000000000000struct Foo { int a; char b[10]; }; int function(struct Foo **f) { return (*f)->a; } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { struct Foo foo1 = { .a = 123, .b = "hello" }; struct Foo *foo2 = &foo1; function(&foo2); return 0; } bpftrace-0.14.0/tests/testprogs/simple_struct.c000066400000000000000000000002501413460502400216130ustar00rootroot00000000000000struct Foo { int m; int n; }; int func(struct Foo *foo) { return foo->m; } int main() { struct Foo foo; foo.m = 2; foo.n = 3; func(&foo); return 0; } bpftrace-0.14.0/tests/testprogs/stack_args.c000066400000000000000000000007171413460502400210470ustar00rootroot00000000000000#define ARG(x) int (x) __attribute((unused)) // Declare enough args that some are placed on the stack void too_many_args(ARG(a), ARG(b), ARG(c), ARG(d), ARG(e), ARG(f), ARG(g), ARG(h), ARG(i), ARG(j)) { } int main(void) { too_many_args(0, 1, 2, 4, 8, 16, 32, 64, 128, 256); return 0; } bpftrace-0.14.0/tests/testprogs/string_args.c000066400000000000000000000002161413460502400212420ustar00rootroot00000000000000void print(char * a, char * b) { (void) a; (void) b; } int main(void) { char sa[] = "hello"; char sb[] = "world"; print(sa, sb); } bpftrace-0.14.0/tests/testprogs/struct_array.c000066400000000000000000000010001413460502400214320ustar00rootroot00000000000000#include #include struct T { uint32_t a; uint32_t b[2]; }; struct W { uint32_t a; struct T t; }; struct C { uint32_t a; void* b; struct W w[10]; }; void clear(struct C* c) { for (int x = 0; x < 10; x++) { c->w[x].t.a = 0; } } int main() { struct C c; c.a = 0x55555555; c.b = (void*)0x55555555; for (int x = 0; x < 10; x++) { c.w[x].a = 100 + x; c.w[x].t.a = x; c.w[x].t.b[0] = 100 - x; c.w[x].t.b[1] = 100 - 2 * x; } clear(&c); } bpftrace-0.14.0/tests/testprogs/struct_walk.c000066400000000000000000000010361413460502400212630ustar00rootroot00000000000000#include #include #include struct C { uint32_t a; uint64_t b; }; void clear(struct C* c, size_t size) { for (size_t t = 0; t < size; t++) { c[t].a = 0; c[t].b = 0; } } void print(struct C* c, size_t size) { uint32_t sum = 0; while (size--) sum += (c++)->a; printf("Sum: %u\n", sum); } int main() { size_t size = 10; struct C* c = (struct C*)malloc(sizeof(struct C) * size); for (size_t t = 0; t < size; t++) { c[t].a = t; c[t].b = 100; } clear(c, size); } bpftrace-0.14.0/tests/testprogs/syscall.c000066400000000000000000000103541413460502400203760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include void usage() { printf("Usage:\n"); printf("\t./syscall []\n"); printf("Supported Syscalls:\n"); printf("\t nanosleep [$N] (default args: 100ns)\n"); #if defined(SYS_open) printf("\t open\n"); #endif printf("\t openat\n"); printf("\t read\n"); printf("\t execve [] (at most 128 arguments)\n"); } int gen_nanosleep(int argc, char *argv[]) { struct timespec req; const char *arg = argv[2]; req.tv_sec = 0; req.tv_nsec = 100; if (argc > 2) { if (!isdigit(*arg) || !isdigit(arg[strlen(arg) - 1])) { printf("Invalid argument: %s; the argument should be a non-negative " "number with no sign\n", arg); return 1; } double time; char tail = '\0'; sscanf(arg, "%le%c", &time, &tail); if (tail != '\0') { printf("Argument '%s' should only contain numerial charactors\n", arg); return 1; } // if time is less than 1 nsec, round up to 1 nsec, as with sleep command if (time > 0 && time < 1) { time = 1; } req.tv_sec = (int)(time / 1e9); req.tv_nsec = (int)(time - req.tv_sec * 1e9); } int r = syscall(SYS_nanosleep, &req, NULL); if (r) { perror("Error in syscall nanosleep"); return 1; } return 0; } // the returned string was created with malloc() // @ path_suffix should start with '/' char *get_tmp_file_path(const char *path_suffix) { const char *tmpdir = getenv("TMPDIR"); if (tmpdir == NULL) { tmpdir = "/tmp"; } int path_len = strlen(tmpdir) + strlen(path_suffix); char *path = (char *)malloc((path_len + 1) * sizeof(char)); memset(path, '\0', path_len + 1); strncat(path, tmpdir, strlen(tmpdir)); strncat(path, path_suffix, strlen(path_suffix)); return path; } int gen_open_openat(bool is_sys_open) { char *file_path = get_tmp_file_path( "/bpftrace_runtime_test_syscall_gen_open_temp"); int fd = -1; if (is_sys_open) { #if defined(SYS_open) fd = syscall(SYS_open, file_path, O_CREAT); #endif } else { fd = syscall(SYS_openat, AT_FDCWD, file_path, O_CREAT); } if (fd < 0) { perror("Error in syscall open/openat"); free(file_path); return 1; } close(fd); remove(file_path); free(file_path); return 0; } int gen_read() { char *file_path = get_tmp_file_path( "/bpftrace_runtime_test_syscall_gen_read_temp"); int fd = open(file_path, O_CREAT); if (fd < 0) { perror("Error in syscall read when creating temp file"); free(file_path); return 1; } char buf[10]; int r = syscall(SYS_read, fd, (void *)buf, 0); close(fd); remove(file_path); free(file_path); if (r < 0) { perror("Error in syscall read"); return 1; } return 0; } int gen_execve(int argc, char *argv[]) { if (argc < 3) { printf("Indicate which process to execute.\n"); return 1; } char *newargv[129] = { NULL }; char *newenv[] = { NULL }; newargv[0] = argv[2]; int arg_nm = argc - 3; if (arg_nm > 128) { printf("Too many arguments: at most 128 arguments, %d given\n", arg_nm); return 1; } for (int i = 0; i < argc; ++i) { newargv[1 + i] = argv[3 + i]; } syscall(SYS_execve, argv[2], newargv, newenv); // execve returns on error perror("Error in syscall execve"); return 1; } int main(int argc, char *argv[]) { if (argc < 2) { usage(); return 1; } const char *syscall_name = argv[1]; int r = 0; if (strcmp("--help", syscall_name) == 0 || strcmp("-h", syscall_name) == 0) { usage(); } else if (strcmp("nanosleep", syscall_name) == 0) { r = gen_nanosleep(argc, argv); } else if (strcmp("openat", syscall_name) == 0) { r = gen_open_openat(false); } #if defined(SYS_open) else if (strcmp("open", syscall_name) == 0) { r = gen_open_openat(true); } #endif else if (strcmp("read", syscall_name) == 0) { r = gen_read(); } else if (strcmp("execve", syscall_name) == 0) { r = gen_execve(argc, argv); } else { printf("%s is not supported yet\n", syscall_name); usage(); r = 1; } return r; } bpftrace-0.14.0/tests/testprogs/uprobe_library.c000066400000000000000000000003121413460502400217350ustar00rootroot00000000000000#include int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { int val; FILE *fd = fopen("/dev/zero", "r"); fread(&val, sizeof(int), 1, fd); fclose(fd); } bpftrace-0.14.0/tests/testprogs/uprobe_negative_retval.c000066400000000000000000000003651413460502400234600ustar00rootroot00000000000000#include __attribute__ ((noinline)) int function1(int x) { return x; } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { for (int x = -120; x <= 100; x++) { function1(x); } return -100; } bpftrace-0.14.0/tests/testprogs/uprobe_test.c000066400000000000000000000011631413460502400212550ustar00rootroot00000000000000#include int GLOBAL_A = 0x55555555; int GLOBAL_B = 0x88888888; int GLOBAL_C = 0x33333333; char GLOBAL_D = 8; struct Foo { int a; char b[10]; }; int function1(int *n, char c __attribute__((unused))) { return *n; } struct Foo *function2(struct Foo *foo1, struct Foo *foo2 __attribute__((unused))) { return foo1; } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { usleep(1000000); int n = 13; char c = 'x'; function1(&n, c); struct Foo foo1 = { .a = 123, .b = "hello" }; struct Foo foo2 = { .a = 456, .b = "world" }; function2(&foo1, &foo2); return 0; } bpftrace-0.14.0/tests/testprogs/usdt_args.c000066400000000000000000000114031413460502400207130ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE1(a, b, d) (void)0 #endif #include #include /* For best results compile using gcc -O1 (or higher) * * Clang likes to always put the argument on stack rather than outputting an * operand to access it directly. * * Note: The macros might not always output the type of probe claimed as this * varies depending on compiler and optimisation settings. * * So I've used macros to make the repeated code easier to fix in the * future. */ #if (defined(__GNUC__) && !defined(__clang__)) #pragma GCC optimize("O1") #else #pragma message("non-gcc compiler: the correct probes might not be installed") #endif #define test_value 0xf7f6f5f4f3f2f1f0 volatile uint64_t one = 1; typedef union all_types { int8_t i_8; uint8_t i_u8; uint16_t i_u16; int16_t i_16; uint32_t i_u32; int32_t i_32; uint64_t i_u64; int64_t i_64; } all_types_t; #ifdef __x86_64__ /* Force the value to be stored in a register, works for both gcc and clang * * PROBE_REG(al, u, 8) expands to: * register uint8_t _reg_u8 asm("al") = (uint8_t)test_value; * DTRACE_PROBE1(usdt_args, reg_u8, _reg_u8) */ #define PROBE_REG(register_, sign, size) \ register sign##int##size##_t _reg_##sign##size asm( \ #register_) = (sign##int##size##_t)test_value; \ DTRACE_PROBE1(usdt_args, reg_##sign##size, _reg_##sign##size) #else /* Force a calculation to get the value into a register * works for gcc (-O1+) but not clang * * PROBE_REG(al, u, 8) expands to: * uint8_t _reg_u8 = (uint8_t)(test_value * one); * DTRACE_PROBE1(usdt_args, reg_u8, _reg_u8); * */ #define PROBE_REG(register_, sign, size) \ sign##int##size##_t _reg_##sign##size = (sign##int##size##_t)(test_value * \ one); \ DTRACE_PROBE1(usdt_args, reg_##sign##size, _reg_##sign##size) #endif /* Read a value of the stack, expect an offset here * Works with optimisation -O1+ * * PROBE_ADDRESS(u, 8) expands to: * array[1].i_u8 = (uint8_t) test_value; * DTRACE_PROBE1(usdt_args, addr_u8, array[1].i_u8) */ #define PROBE_ADDRESS(sign, size) \ array[1].i_##sign##size = (sign##int##size##_t)test_value; \ DTRACE_PROBE1(usdt_args, addr_##sign##size, array[1].i_##sign##size) /* Read a value of the stack, expect an offset here * Works only with gcc optimisation -O1+ * * PROBE_INDEX(u, 8) expands to: * DTRACE_PROBE1(usdt_args, index_u8, array[i].i_u8) */ #define PROBE_INDEX(sign, size) \ DTRACE_PROBE1(usdt_args, index_##sign##size, array[i].i_##sign##size) int main(int argc, char **argv) { (void)argv; volatile all_types_t array[10]; if (argc > 1) // If we don't have Systemtap headers, we should skip USDT tests. Returning 1 // can be used as validation in the REQUIRE #ifndef HAVE_SYSTEMTAP_SYS_SDT_H return 1; #else return 0; #endif for (volatile size_t i = 0; i < sizeof(array) / sizeof(array[0]); i++) { array[i].i_u64 = test_value; } #ifdef HAVE_SYSTEMTAP_SYS_SDT_H /* Constants */ DTRACE_PROBE1(usdt_args, const_u64, (uint64_t)test_value); DTRACE_PROBE1(usdt_args, const_64, (int64_t)test_value); DTRACE_PROBE1(usdt_args, const_u32, (uint32_t)test_value); DTRACE_PROBE1(usdt_args, const_32, (int32_t)test_value); DTRACE_PROBE1(usdt_args, const_u16, (uint16_t)test_value); DTRACE_PROBE1(usdt_args, const_16, (int16_t)test_value); DTRACE_PROBE1(usdt_args, const_u8, (uint8_t)test_value); DTRACE_PROBE1(usdt_args, const_8, (int8_t)test_value); /* Direct register reads - start from 64 and work down * to verify the correct number of bytes are read */ PROBE_REG(rax, u, 64); PROBE_REG(rax, , 64); PROBE_REG(eax, u, 32); PROBE_REG(eax, , 32); PROBE_REG(ax, u, 16); PROBE_REG(ax, , 16); PROBE_REG(al, u, 8); PROBE_REG(al, , 8); /* Base address most likely with and offset(aka. displacement) */ PROBE_ADDRESS(u, 64); PROBE_ADDRESS(, 64); PROBE_ADDRESS(u, 32); PROBE_ADDRESS(, 32); PROBE_ADDRESS(u, 16); PROBE_ADDRESS(, 16); PROBE_ADDRESS(u, 8); PROBE_ADDRESS(, 8); /* Base address + offset + (index * scale) */ for (volatile int i = 7; i <= 7; i++) { PROBE_INDEX(u, 64); PROBE_INDEX(, 64); PROBE_INDEX(u, 32); PROBE_INDEX(, 32); PROBE_INDEX(u, 16); PROBE_INDEX(, 16); PROBE_INDEX(u, 8); PROBE_INDEX(, 8); } /* TLS not yet supported, need label and segment support */ #if 0 volatile static __thread uint64_t tls_64 = (uint64_t)test_value; DTRACE_PROBE1(usdt_args, tls_u64, tls_64); #endif #endif /* HAVE_SYSTEMTAP_SYS_SDT_H */ return 0; } bpftrace-0.14.0/tests/testprogs/usdt_inlined.c000066400000000000000000000017121413460502400214030ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include #include #include __attribute__((always_inline)) inline static void myclock(int probe_num) { // Volatile forces reading directly from the stack so that // the probe's argument is not saved as a constant value. volatile int on_stack = probe_num; (void)on_stack; struct timeval tv; gettimeofday(&tv, NULL); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, on_stack); } static void mywrapper() { myclock(100); } static void loop() { while (1) { myclock(999); mywrapper(); sleep(1); } } int main(int argc, char **argv __attribute__((unused))) { if (argc > 1) // If we don't have Systemtap headers, we should skip USDT tests. Returning 1 // can be used as validation in the REQUIRE #ifndef HAVE_SYSTEMTAP_SYS_SDT_H return 1; #else return 0; #endif loop(); return 0; } bpftrace-0.14.0/tests/testprogs/usdt_lib.c000066400000000000000000000005551413460502400205330ustar00rootroot00000000000000#include "usdt_tp.h" #include int main(int argc, char **argv __attribute__((unused))) { if (argc > 1) // If we don't have Systemtap headers, we should skip USDT tests. Returning 1 // can be used as validation in the REQUIRE #ifndef HAVE_SYSTEMTAP_SYS_SDT_H return 1; #else return 0; #endif while (1) { myclock(); } return 0; } bpftrace-0.14.0/tests/testprogs/usdt_quoted_probe.c000066400000000000000000000010431413460502400224460ustar00rootroot00000000000000#include #ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE1(a, b, c) (void)0 #endif int main(int argc, char **argv __attribute__((unused))) { if (argc > 1) // If we don't have Systemtap headers, we should skip USDT tests. Returning 1 // can be used as validation in the REQUIRE #ifndef HAVE_SYSTEMTAP_SYS_SDT_H return 1; #else return 0; #endif int a = 1; // For some reason some compilers think `a` is unused (void)a; while (1) DTRACE_PROBE1(test, "probe1", a); return 0; } bpftrace-0.14.0/tests/testprogs/usdt_semaphore_test.c000066400000000000000000000017721413460502400230110ustar00rootroot00000000000000#define _SDT_HAS_SEMAPHORES 1 #ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include #include #include __extension__ unsigned short tracetest_testprobe_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))) __attribute__ ((visibility ("hidden"))); static long myclock() { char buffer[100]; struct timeval tv; gettimeofday(&tv, NULL); sprintf(buffer, "tracetest_testprobe_semaphore: %d\n", tracetest_testprobe_semaphore); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, buffer); return tv.tv_sec; } int main(int argc, char **argv __attribute__((unused))) { if (argc > 1) // If we don't have Systemtap headers, we should skip USDT tests. Returning 1 can be used as validation in the REQUIRE #ifndef HAVE_SYSTEMTAP_SYS_SDT_H return 1; #else return 0; #endif while (1) { myclock(); // Sleep is necessary to not overflow perf buffer usleep(1000); } return 0; } bpftrace-0.14.0/tests/testprogs/usdt_sized_args.c000066400000000000000000000006041413460502400221120ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE1(a, b, c) (void)0 #endif #include int main() { uint32_t a = 0xdeadbeef; uint32_t b = 1; uint64_t c = UINT64_MAX; (void)a; (void)b; (void)c; while (1) { DTRACE_PROBE1(test, probe1, a); DTRACE_PROBE1(test, probe2, b); DTRACE_PROBE1(test, probe3, c); } return 0; } bpftrace-0.14.0/tests/testprogs/usdt_test.c000066400000000000000000000014211413460502400207350ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include #include #include static long myclock() { struct timeval tv; gettimeofday(&tv, NULL); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, "Hello world"); DTRACE_PROBE2(tracetest, testprobe2, tv.tv_sec, "Hello world2"); DTRACE_PROBE2(tracetest2, testprobe2, tv.tv_sec, "Hello world3"); return tv.tv_sec; } int main(int argc, char **argv __attribute__((unused))) { if (argc > 1) // If we don't have Systemtap headers, we should skip USDT tests. Returning 1 can be used as validation in the REQUIRE #ifndef HAVE_SYSTEMTAP_SYS_SDT_H return 1; #else return 0; #endif while (1) { myclock(); } return 0; } bpftrace-0.14.0/tests/testprogs/wait4_ru.c000066400000000000000000000004661413460502400204650ustar00rootroot00000000000000#include #include #include #include #include #include #include int main(void) { struct rusage rusage; pid_t pid; pid = fork(); if (pid == 0) { exit(0); } wait4(pid, NULL, 0, &rusage); return 0; } bpftrace-0.14.0/tests/testprogs/watchpoint.c000066400000000000000000000006061413460502400211030ustar00rootroot00000000000000#include #include #include #include int main() { volatile void* addr = mmap( (void*)0x10000000, 2 << 20, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if ((long)addr < 0) { perror("mmap"); return 1; } uint8_t i = 0; while (1) { *((volatile uint8_t*)addr) = i++; } } bpftrace-0.14.0/tests/testprogs/watchpoint_func.c000066400000000000000000000004211413460502400221110ustar00rootroot00000000000000#include #include #include __attribute__((noinline)) void increment(__attribute__((unused)) int _, int *i) { (*i)++; } int main() { int *i = malloc(sizeof(int)); while (1) { increment(0, i); (*i)++; usleep(1000); } } bpftrace-0.14.0/tests/testprogs/watchpoint_func_many_probes.c000066400000000000000000000003261413460502400245130ustar00rootroot00000000000000#include #include #include __attribute__((noinline)) void increment(int *i) { (*i)++; } int main() { for (int i = 0; i < 20; ++i) { increment(malloc(sizeof(int))); } } bpftrace-0.14.0/tests/testprogs/watchpoint_func_wildcard.c000066400000000000000000000020601413460502400237630ustar00rootroot00000000000000#include #include #include #define GEN_FUNC(x) \ __attribute__((noinline)) void increment_##x(int *i) \ { \ (*i)++; \ } #define CALL_FUNC(x) increment_##x(malloc(sizeof(int))) GEN_FUNC(0) GEN_FUNC(1) GEN_FUNC(2) GEN_FUNC(3) GEN_FUNC(4) GEN_FUNC(5) GEN_FUNC(6) GEN_FUNC(7) GEN_FUNC(8) GEN_FUNC(9) GEN_FUNC(10) GEN_FUNC(11) GEN_FUNC(12) GEN_FUNC(13) GEN_FUNC(14) GEN_FUNC(15) GEN_FUNC(16) GEN_FUNC(17) GEN_FUNC(18) GEN_FUNC(19) GEN_FUNC(20) int main() { CALL_FUNC(0); CALL_FUNC(1); CALL_FUNC(2); CALL_FUNC(3); CALL_FUNC(4); CALL_FUNC(5); CALL_FUNC(6); CALL_FUNC(7); CALL_FUNC(8); CALL_FUNC(9); CALL_FUNC(10); CALL_FUNC(11); CALL_FUNC(12); CALL_FUNC(13); CALL_FUNC(14); CALL_FUNC(15); CALL_FUNC(16); CALL_FUNC(17); CALL_FUNC(18); CALL_FUNC(19); CALL_FUNC(20); } bpftrace-0.14.0/tests/testprogs/watchpoint_unwatch.c000066400000000000000000000005171413460502400226350ustar00rootroot00000000000000#include #include #include __attribute__((noinline)) void increment(int *i) { (*i)++; } int main() { int *i = malloc(sizeof(int)); increment(i); // Yes, this sleep sucks but unwatch is async and we have // no way to delay the bpf prog sleep(1); (*i)++; (*i)++; (*i)++; (*i)++; } bpftrace-0.14.0/tests/tools-parsing-test.sh000077500000000000000000000013361413460502400206430ustar00rootroot00000000000000#!/bin/bash set +e; if [[ $EUID -ne 0 ]]; then >&2 echo "Must be run as root" exit 1 fi DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" BPFTRACE_EXECUTABLE=${BPFTRACE_EXECUTABLE:-$DIR/../src/bpftrace}; EXIT_STATUS=0; TOOLDIR="" function tooldir() { for dir in "../../tools" "/vagrant/tools"; do if [[ -d "$dir" ]]; then TOOLDIR="$dir" return fi done >&2 echo "Tool dir not found" exit 1 } tooldir for f in "$TOOLDIR"/*.bt; do if $BPFTRACE_EXECUTABLE --unsafe -d $f 2>/dev/null >/dev/null; then echo "$f passed" else echo "$f failed"; $BPFTRACE_EXECUTABLE --unsafe -d $f; EXIT_STATUS=1; fi done exit $EXIT_STATUS bpftrace-0.14.0/tests/tracepoint_format_parser.cpp000066400000000000000000000233141413460502400223260ustar00rootroot00000000000000#include "tracepoint_format_parser.h" #include "mocks.h" #include "gtest/gtest.h" #include using namespace testing; namespace bpftrace { namespace test { namespace tracepoint_format_parser { class MockTracepointFormatParser : public TracepointFormatParser { public: static std::string get_tracepoint_struct_public(std::istream &format_file, const std::string &category, const std::string &event_name, BPFtrace &bpftrace) { return get_tracepoint_struct(format_file, category, event_name, bpftrace); } }; TEST(tracepoint_format_parser, tracepoint_struct) { std::string input = "name: sys_enter_read\n" "ID: 650\n" "format:\n" " field:unsigned short common_type; offset:0; size:2; signed:0;\n" " field:unsigned char common_flags; offset:2; size:1; signed:0;\n" " field:unsigned char common_preempt_count; offset:3; size:1; signed:0;\n" " field:int common_pid; offset:4; size:4; signed:1;\n" "\n" " field:int __syscall_nr; offset:8; size:4; signed:1;\n" " field:unsigned int fd; offset:16; size:8; signed:0;\n" " field:char * buf; offset:24; size:8; signed:0;\n" " field:size_t count; offset:32; size:8; signed:0;\n" "\n" "print fmt: \"fd: 0x%08lx, buf: 0x%08lx, count: 0x%08lx\", ((unsigned long)(REC->fd)), ((unsigned long)(REC->buf)), ((unsigned long)(REC->count))\n"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " unsigned short common_type;\n" " unsigned char common_flags;\n" " unsigned char common_preempt_count;\n" " int common_pid;\n" " int __syscall_nr;\n" " char __pad_12;\n" " char __pad_13;\n" " char __pad_14;\n" " char __pad_15;\n" " u64 fd;\n" " char * buf;\n" " size_t count;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, array) { std::string input = " field:char char_array[8]; offset:0; size:8; signed:1;\n" " field:int int_array[2]; offset:8; size:8; signed:1;\n"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " char char_array[8];\n" " int int_array[2];\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, data_loc) { std::string input = " field:__data_loc char[] msg; offset:8; size:4; signed:1;"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " __attribute__((annotate(\"tp_data_loc\"))) int msg;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, adjust_integer_types) { std::string input = " field:int arr[8]; offset:0; size:32; signed:1;\n" " field:int int_a; offset:0; size:4; signed:1;\n" " field:int int_b; offset:0; size:8; signed:1;\n" " field:u32 u32_a; offset:0; size:4; signed:0;\n" " field:u32 u32_b; offset:0; size:8; signed:0;\n" " field:unsigned int uint_a; offset:0; size:4; signed:0;\n" " field:unsigned int uint_b; offset:0; size:8; signed:0;\n" " field:unsigned unsigned_a; offset:0; size:4; signed:0;\n" " field:unsigned unsigned_b; offset:0; size:8; signed:0;\n" " field:uid_t uid_a; offset:0; size:4; signed:0;\n" " field:uid_t uid_b; offset:0; size:8; signed:0;\n" " field:gid_t gid_a; offset:0; size:4; signed:0;\n" " field:gid_t gid_b; offset:0; size:8; signed:0;\n" " field:pid_t pid_a; offset:0; size:4; signed:1;\n" " field:pid_t pid_b; offset:0; size:8; signed:0;\n"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " int arr[8];\n" " int int_a;\n" " s64 int_b;\n" " u32 u32_a;\n" " u64 u32_b;\n" " unsigned int uint_a;\n" " u64 uint_b;\n" " unsigned unsigned_a;\n" " u64 unsigned_b;\n" " uid_t uid_a;\n" " u64 uid_b;\n" " gid_t gid_a;\n" " u64 gid_b;\n" " pid_t pid_a;\n" " u64 pid_b;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, padding) { std::string input = " field:unsigned short common_type; offset:0; size:2; " "signed:0;\n" " field:unsigned char common_flags; offset:2; size:1; " "signed:0;\n" " field:unsigned char common_preempt_count; offset:3; " "size:1; signed:0;\n" " field:int common_pid; offset:4; size:4; signed:1;\n" " field:unsigned char common_migrate_disable; offset:8; " "size:1; signed:0;\n" " field:unsigned char common_preempt_lazy_count; offset:9; " "size:1; signed:0;\n" " field:char comm[16]; offset:12; size:16; signed:1;\n" " field:pid_t pid; offset:28; size:4; signed:1;\n" " field:int prio; offset:32; size:4; signed:1;\n" " field:int success; offset:36; size:4; signed:1;\n" " field:int target_cpu; offset:40; size:4; signed:1;\n"; std::string expected = "struct _tracepoint_sched_sched_wakeup\n" "{\n" " unsigned short common_type;\n" " unsigned char common_flags;\n" " unsigned char common_preempt_count;\n" " int common_pid;\n" " unsigned char common_migrate_disable;\n" " unsigned char common_preempt_lazy_count;\n" " char __pad_10;\n" " char __pad_11;\n" " char comm[16];\n" " pid_t pid;\n" " int prio;\n" " int success;\n" " int target_cpu;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "sched", "sched_wakeup", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, tracepoint_struct_btf) { std::string input = "name: sys_enter_read\n" "ID: 650\n" "format:\n" " field:unsigned short common_type; offset:0; size:2; " "signed:0;\n" " field:unsigned char common_flags; offset:2; size:1; " "signed:0;\n" " field:unsigned char common_preempt_count; offset:3; " "size:1; signed:0;\n" " field:int common_pid; offset:4; size:4; signed:1;\n" "\n" " field:int __syscall_nr; offset:8; size:4; signed:1;\n" " field:unsigned int fd; offset:16; size:8; signed:0;\n" " field:char * buf; offset:24; size:8; signed:0;\n" " field:size_t count; offset:32; size:8; signed:0;\n" "\n" "print fmt: \"fd: 0x%08lx, buf: 0x%08lx, count: 0x%08lx\", ((unsigned " "long)(REC->fd)), ((unsigned long)(REC->buf)), ((unsigned " "long)(REC->count))\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); // Check that BTF types are populated EXPECT_THAT(bpftrace.btf_set_, Contains("unsigned short")); EXPECT_THAT(bpftrace.btf_set_, Contains("unsigned char")); EXPECT_THAT(bpftrace.btf_set_, Contains("int")); EXPECT_THAT(bpftrace.btf_set_, Contains("u64")); EXPECT_THAT(bpftrace.btf_set_, Contains("char *")); EXPECT_THAT(bpftrace.btf_set_, Contains("size_t")); } TEST(tracepoint_format_parser, args_field_access) { // Test computing the level of nested structs accessed from tracepoint args BPFtrace bpftrace; Driver driver(bpftrace); ast::TracepointArgsVisitor visitor; EXPECT_EQ(driver.parse_str("BEGIN { args->f1->f2->f3 }"), 0); visitor.visit(*driver.root_->probes->at(0)); EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, 3); // Should work via intermediary variable, too EXPECT_EQ(driver.parse_str("BEGIN { $x = args->f1; $x->f2->f3 }"), 0); visitor.visit(*driver.root_->probes->at(0)); EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, 3); // "args" used without field access => level should be 0 EXPECT_EQ(driver.parse_str("BEGIN { args }"), 0); visitor.visit(*driver.root_->probes->at(0)); EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, 0); // "args" not used => level should be -1 EXPECT_EQ(driver.parse_str("BEGIN { x->f1->f2->f3 }"), 0); visitor.visit(*driver.root_->probes->at(0)); EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, -1); } } // namespace tracepoint_format_parser } // namespace test } // namespace bpftrace bpftrace-0.14.0/tests/utils.cpp000066400000000000000000000147531413460502400164010ustar00rootroot00000000000000#include "utils.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include #include namespace bpftrace { namespace test { namespace utils { TEST(utils, split_string) { std::vector tokens_empty = {}; std::vector tokens_one_empty = {""}; std::vector tokens_two_empty = {"", ""}; std::vector tokens_f = {"", "f"}; std::vector tokens_foo_bar = {"foo", "bar"}; std::vector tokens_empty_foo_bar = {"", "foo", "bar"}; std::vector tokens_empty_foo_empty_bar = {"", "foo", "", "bar"}; std::vector tokens_empty_foo_bar_biz = {"", "foo", "bar", "biz"}; EXPECT_EQ(split_string("", '-'), tokens_empty); EXPECT_EQ(split_string("-", '-'), tokens_one_empty); EXPECT_EQ(split_string("--", '-'), tokens_two_empty); EXPECT_EQ(split_string("--", '-', true), tokens_empty); EXPECT_EQ(split_string("-f-", '-'), tokens_f); EXPECT_EQ(split_string("-foo-bar-", '-'), tokens_empty_foo_bar); EXPECT_EQ(split_string("-foo--bar-", '-'), tokens_empty_foo_empty_bar); EXPECT_EQ(split_string("-foo-bar-biz-", '-'), tokens_empty_foo_bar_biz); EXPECT_EQ(split_string("-foo-bar", '-'), tokens_empty_foo_bar); EXPECT_EQ(split_string("foo-bar-", '-'), tokens_foo_bar); EXPECT_EQ(split_string("foo-bar", '-'), tokens_foo_bar); } TEST(utils, wildcard_match) { std::vector tokens_not = {"not"}; std::vector tokens_bar = {"bar"}; std::vector tokens_bar_not = {"bar", "not"}; std::vector tokens_foo = {"foo"}; std::vector tokens_biz = {"biz"}; std::vector tokens_foo_biz = {"foo", "biz"}; // start: true, end: true EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, true, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, true, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, true, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, true, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, true, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, true, true), true); // start: false, end: true EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, false, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, false, true), true); // start: true, end: false EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, true, false), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, true, false), true); // start: false, end: false EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, false, false), true); } static void symlink_test_binary(const std::string& destination) { if (symlink("/proc/self/exe", destination.c_str())) { throw std::runtime_error("Couldn't symlink /proc/self/exe to " + destination + ": " + strerror(errno)); } } static std::string get_working_path() { char cwd_path[PATH_MAX]; if (::getcwd(cwd_path, PATH_MAX) == nullptr) { throw std::runtime_error( "getting current working directory for tests failed"); } return std::string(cwd_path); } TEST(utils, resolve_binary_path) { std::string path = "/tmp/bpftrace-test-utils-XXXXXX"; if (::mkdtemp(&path[0]) == nullptr) { throw std::runtime_error("creating temporary path for tests failed"); } // We need real elf executables, linking test binary allows us to do that // without additional dependencies. symlink_test_binary(path + "/executable"); symlink_test_binary(path + "/executable2"); int fd; fd = open((path + "/nonexecutable").c_str(), O_CREAT, S_IRUSR); close(fd); fd = open((path + "/nonexecutable2").c_str(), O_CREAT, S_IRUSR); close(fd); std::vector paths_empty = {}; std::vector paths_one_executable = {path + "/executable"}; std::vector paths_all_executables = {path + "/executable", path + "/executable2"}; EXPECT_EQ(resolve_binary_path(path + "/does/not/exist"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/does/not/exist*"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/nonexecutable"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/nonexecutable*"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/executable"), paths_one_executable); EXPECT_EQ(resolve_binary_path(path + "/executable*"), paths_all_executables); EXPECT_EQ(resolve_binary_path(path + "/*executable*"), paths_all_executables); exec_system(("rm -rf " + path).c_str()); } TEST(utils, abs_path) { std::string path = "/tmp/bpftrace-test-utils-XXXXXX"; std::string rel_file = "bpftrace-test-utils-abs-path"; if (::mkdtemp(&path[0]) == nullptr) { throw std::runtime_error("creating temporary path for tests failed"); } int fd; fd = open((path + "/somefile").c_str(), O_CREAT, S_IRUSR); close(fd); fd = open(rel_file.c_str(), O_CREAT, S_IRUSR); close(fd); // Translates absolute path with '../..' EXPECT_EQ(abs_path(path + "/../.." + path + "/somefile"), path + "/somefile"); // Translates relative path with './' EXPECT_EQ(abs_path("./" + rel_file), get_working_path() + "/" + rel_file); // /proc//root path returned as is (and doesn't throw) EXPECT_NO_THROW( abs_path(std::string("/proc/1/root/usr/local/bin/usdt_test.so"))); EXPECT_EQ(abs_path(std::string("/proc/1/root/usr/local/bin/usdt_test.so")), std::string("/proc/1/root/usr/local/bin/usdt_test.so")); remove(rel_file.c_str()); } } // namespace utils } // namespace test } // namespace bpftrace bpftrace-0.14.0/tools/000077500000000000000000000000001413460502400145215ustar00rootroot00000000000000bpftrace-0.14.0/tools/CMakeLists.txt000066400000000000000000000004461413460502400172650ustar00rootroot00000000000000file(GLOB BT_FILES *.bt) file(GLOB TXT_FILES *.txt) list(REMOVE_ITEM TXT_FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) install(FILES ${BT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools) install(FILES ${TXT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools/doc) bpftrace-0.14.0/tools/bashreadline.bt000077500000000000000000000012721413460502400174760ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * bashreadline Print entered bash commands from all running shells. * For Linux, uses bpftrace and eBPF. * * This works by tracing the readline() function using a uretprobe (uprobes). * * USAGE: bashreadline.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing bash commands... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %s\n", "TIME", "PID", "COMMAND"); } uretprobe:/bin/bash:readline { time("%H:%M:%S "); printf("%-6d %s\n", pid, str(retval)); } bpftrace-0.14.0/tools/bashreadline_example.txt000066400000000000000000000013221413460502400214140ustar00rootroot00000000000000Demonstrations of bashreadline, the Linux bpftrace/eBPF version. This prints bash commands from all running bash shells on the system. For example: # ./bashreadline.bt Attaching 2 probes... Tracing bash commands... Hit Ctrl-C to end. TIME PID COMMAND 06:40:06 5526 df -h 06:40:09 5526 ls -l 06:40:18 5526 echo hello bpftrace 06:40:42 5526 echooo this is a failed command, but we can see it anyway ^C The entered command may fail. This is just showing what command lines were entered interactively for bash to process. It works by tracing the return of the readline() function using uprobes (specifically a uretprobe). There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/biolatency.bt000077500000000000000000000011331413460502400172020ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * biolatency.bt Block I/O latency as a histogram. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 13-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing block device I/O... Hit Ctrl-C to end.\n"); } kprobe:blk_account_io_start { @start[arg0] = nsecs; } kprobe:blk_account_io_done /@start[arg0]/ { @usecs = hist((nsecs - @start[arg0]) / 1000); delete(@start[arg0]); } END { clear(@start); } bpftrace-0.14.0/tools/biolatency_example.txt000066400000000000000000000031561413460502400211330ustar00rootroot00000000000000Demonstrations of biolatency, the Linux BPF/bpftrace version. This traces block I/O, and shows latency as a power-of-2 histogram. For example: # ./biolatency.bt Attaching 3 probes... Tracing block device I/O... Hit Ctrl-C to end. ^C @usecs: [256, 512) 2 | | [512, 1K) 10 |@ | [1K, 2K) 426 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [2K, 4K) 230 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4K, 8K) 9 |@ | [8K, 16K) 128 |@@@@@@@@@@@@@@@ | [16K, 32K) 68 |@@@@@@@@ | [32K, 64K) 0 | | [64K, 128K) 0 | | [128K, 256K) 10 |@ | While tracing, this shows that 426 block I/O had a latency of between 1K and 2K usecs (1024 and 2048 microseconds), which is between 1 and 2 milliseconds. There are also two modes visible, one between 1 and 2 milliseconds, and another between 8 and 16 milliseconds: this sounds like cache hits and cache misses. There were also 10 I/O with latency 128 to 256 ms: outliers. Other tools and instrumentation, like biosnoop.bt, can shed more light on those outliers. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.14.0/tools/biosnoop.bt000077500000000000000000000020031413460502400166760ustar00rootroot00000000000000#!/usr/bin/env bpftrace #include /* * biosnoop.bt Block I/O tracing tool, showing per I/O latency. * For Linux, uses bpftrace, eBPF. * * TODO: switch to block tracepoints. Add offset and size columns. * * This is a bpftrace version of the bcc tool of the same name. * * 15-Nov-2017 Brendan Gregg Created this. */ BEGIN { printf("%-12s %-7s %-16s %-6s %7s\n", "TIME(ms)", "DISK", "COMM", "PID", "LAT(ms)"); } kprobe:blk_account_io_start { @start[arg0] = nsecs; @iopid[arg0] = pid; @iocomm[arg0] = comm; @disk[arg0] = ((struct request *)arg0)->rq_disk->disk_name; } kprobe:blk_account_io_done /@start[arg0] != 0 && @iopid[arg0] != 0 && @iocomm[arg0] != ""/ { $now = nsecs; printf("%-12u %-7s %-16s %-6d %7d\n", elapsed / 1e6, @disk[arg0], @iocomm[arg0], @iopid[arg0], ($now - @start[arg0]) / 1e6); delete(@start[arg0]); delete(@iopid[arg0]); delete(@iocomm[arg0]); delete(@disk[arg0]); } END { clear(@start); clear(@iopid); clear(@iocomm); clear(@disk); } bpftrace-0.14.0/tools/biosnoop_example.txt000066400000000000000000000040101413460502400206200ustar00rootroot00000000000000Demonstrations of biosnoop, the Linux BPF/bpftrace version. This traces block I/O, and shows the issuing process (at least, the process that was on-CPU at the time of queue insert) and the latency of the I/O: # ./biosnoop.bt Attaching 4 probes... TIME(ms) DISK COMM PID LAT(ms) 611 nvme0n1 bash 4179 10 611 nvme0n1 cksum 4179 0 627 nvme0n1 cksum 4179 15 641 nvme0n1 cksum 4179 13 644 nvme0n1 cksum 4179 3 658 nvme0n1 cksum 4179 13 673 nvme0n1 cksum 4179 14 686 nvme0n1 cksum 4179 13 701 nvme0n1 cksum 4179 14 710 nvme0n1 cksum 4179 8 717 nvme0n1 cksum 4179 6 728 nvme0n1 cksum 4179 10 735 nvme0n1 cksum 4179 6 751 nvme0n1 cksum 4179 10 758 nvme0n1 cksum 4179 17 783 nvme0n1 cksum 4179 12 796 nvme0n1 cksum 4179 25 802 nvme0n1 cksum 4179 32 [...] This output shows the cksum process was issuing block I/O, which were completing with around 12 milliseconds of latency. Each block I/O event is printed out, with a completion time as the first column, measured from program start. An example of some background flushing: # ./biosnoop.bt Attaching 4 probes... TIME(ms) DISK COMM PID LAT(ms) 2966 nvme0n1 jbd2/nvme0n1-8 615 0 2967 nvme0n1 jbd2/nvme0n1-8 615 0 [...] There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides more fields. bpftrace-0.14.0/tools/biostacks.bt000077500000000000000000000015641413460502400170430ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * biostacks - Show disk I/O latency with initialization stacks. * * See BPF Performance Tools, Chapter 9, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 19-Mar-2019 Brendan Gregg Created this. */ BEGIN { printf("Tracing block I/O with init stacks. Hit Ctrl-C to end.\n"); } kprobe:blk_account_io_start { @reqstack[arg0] = kstack; @reqts[arg0] = nsecs; } kprobe:blk_start_request, kprobe:blk_mq_start_request /@reqts[arg0]/ { @usecs[@reqstack[arg0]] = hist(nsecs - @reqts[arg0]); delete(@reqstack[arg0]); delete(@reqts[arg0]); } END { clear(@reqstack); clear(@reqts); } bpftrace-0.14.0/tools/biostacks_example.txt000066400000000000000000000035721413460502400207660ustar00rootroot00000000000000Demonstrations of biostacks, the Linux BCC/eBPF version. This tool shows block I/O latency as a histogram, with the kernel stack trace that initiated the I/O. This can help explain disk I/O that is not directly requested by applications (eg, metadata reads on writes, resilvering, etc). For example: # ./biostacks.bt Attaching 5 probes... Tracing block I/O with init stacks. Hit Ctrl-C to end. ^C @usecs[ blk_account_io_start+1 blk_mq_make_request+1102 generic_make_request+292 submit_bio+115 _xfs_buf_ioapply+798 xfs_buf_submit+101 xlog_bdstrat+43 xlog_sync+705 xlog_state_release_iclog+108 _xfs_log_force+542 xfs_log_force+44 xfsaild+428 kthread+289 ret_from_fork+53 ]: [64K, 128K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [...] @usecs[ blk_account_io_start+1 blk_mq_make_request+707 generic_make_request+292 submit_bio+115 xfs_add_to_ioend+455 xfs_do_writepage+758 write_cache_pages+524 xfs_vm_writepages+190 do_writepages+75 __writeback_single_inode+69 writeback_sb_inodes+481 __writeback_inodes_wb+103 wb_writeback+625 wb_workfn+384 process_one_work+478 worker_thread+50 kthread+289 ret_from_fork+53 ]: [8K, 16K) 560 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [16K, 32K) 218 |@@@@@@@@@@@@@@@@@@@@ | [32K, 64K) 26 |@@ | [64K, 128K) 2 | | [128K, 256K) 53 |@@@@ | [256K, 512K) 60 |@@@@@ | This output shows the most frequent stack was XFS writeback, with latencies between 8 and 512 microseconds. The other stack included here shows an XFS log sync. bpftrace-0.14.0/tools/bitesize.bt000077500000000000000000000010711413460502400166700ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * bitesize Show disk I/O size as a histogram. * For Linux, uses bpftrace and eBPF. * * USAGE: bitesize.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 07-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing block device I/O... Hit Ctrl-C to end.\n"); } tracepoint:block:block_rq_issue { @[args->comm] = hist(args->bytes); } END { printf("\nI/O size (bytes) histograms by process name:"); } bpftrace-0.14.0/tools/bitesize_example.txt000066400000000000000000000056731413460502400206260ustar00rootroot00000000000000Demonstrations of bitesize, the Linux bpftrace/eBPF version. This traces disk I/O via the block I/O interface, and prints a summary of I/O sizes as histograms for each process name. For example: # ./bitesize.bt Attaching 3 probes... Tracing block device I/O... Hit Ctrl-C to end. ^C I/O size (bytes) histograms by process name: @[cleanup]: [4K, 8K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @[postdrop]: [4K, 8K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @[jps]: [4K, 8K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [8K, 16K) 0 | | [16K, 32K) 0 | | [32K, 64K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @[kworker/2:1H]: [0] 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 0 | | [8K, 16K) 0 | | [16K, 32K) 0 | | [32K, 64K) 0 | | [64K, 128K) 1 |@@@@@@@@@@@@@@@@@ | @[jbd2/nvme0n1-8]: [4K, 8K) 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 0 | | [16K, 32K) 0 | | [32K, 64K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [64K, 128K) 1 |@@@@@@@@@@@@@@@@@ | @[dd]: [16K, 32K) 921 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| The most active process while tracing was "dd", which issues 921 I/O between 16 Kbytes and 32 Kbytes in size. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/capable.bt000077500000000000000000000036071413460502400164500ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * capable Trace security capabilitiy checks (cap_capable()). * For Linux, uses bpftrace and eBPF. * * USAGE: capable.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing cap_capable syscalls... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %-6s %-16s %-4s %-20s AUDIT\n", "TIME", "UID", "PID", "COMM", "CAP", "NAME"); @cap[0] = "CAP_CHOWN"; @cap[1] = "CAP_DAC_OVERRIDE"; @cap[2] = "CAP_DAC_READ_SEARCH"; @cap[3] = "CAP_FOWNER"; @cap[4] = "CAP_FSETID"; @cap[5] = "CAP_KILL"; @cap[6] = "CAP_SETGID"; @cap[7] = "CAP_SETUID"; @cap[8] = "CAP_SETPCAP"; @cap[9] = "CAP_LINUX_IMMUTABLE"; @cap[10] = "CAP_NET_BIND_SERVICE"; @cap[11] = "CAP_NET_BROADCAST"; @cap[12] = "CAP_NET_ADMIN"; @cap[13] = "CAP_NET_RAW"; @cap[14] = "CAP_IPC_LOCK"; @cap[15] = "CAP_IPC_OWNER"; @cap[16] = "CAP_SYS_MODULE"; @cap[17] = "CAP_SYS_RAWIO"; @cap[18] = "CAP_SYS_CHROOT"; @cap[19] = "CAP_SYS_PTRACE"; @cap[20] = "CAP_SYS_PACCT"; @cap[21] = "CAP_SYS_ADMIN"; @cap[22] = "CAP_SYS_BOOT"; @cap[23] = "CAP_SYS_NICE"; @cap[24] = "CAP_SYS_RESOURCE"; @cap[25] = "CAP_SYS_TIME"; @cap[26] = "CAP_SYS_TTY_CONFIG"; @cap[27] = "CAP_MKNOD"; @cap[28] = "CAP_LEASE"; @cap[29] = "CAP_AUDIT_WRITE"; @cap[30] = "CAP_AUDIT_CONTROL"; @cap[31] = "CAP_SETFCAP"; @cap[32] = "CAP_MAC_OVERRIDE"; @cap[33] = "CAP_MAC_ADMIN"; @cap[34] = "CAP_SYSLOG"; @cap[35] = "CAP_WAKE_ALARM"; @cap[36] = "CAP_BLOCK_SUSPEND"; @cap[37] = "CAP_AUDIT_READ"; @cap[38] = "CAP_PERFMON"; @cap[39] = "CAP_BPF"; @cap[40] = "CAP_CHECKPOINT_RESTORE"; } kprobe:cap_capable { $cap = arg2; $audit = arg3; time("%H:%M:%S "); printf("%-6d %-6d %-16s %-4d %-20s %d\n", uid, pid, comm, $cap, @cap[$cap], $audit); } END { clear(@cap); } bpftrace-0.14.0/tools/capable_example.txt000066400000000000000000000051431413460502400203670ustar00rootroot00000000000000Demonstrations of capable, the Linux bpftrace/eBPF version. capable traces calls to the kernel cap_capable() function, which does security capability checks, and prints details for each call. For example: # ./capable.bt TIME UID PID COMM CAP NAME AUDIT 22:11:23 114 2676 snmpd 12 CAP_NET_ADMIN 1 22:11:23 0 6990 run 24 CAP_SYS_RESOURCE 1 22:11:23 0 7003 chmod 3 CAP_FOWNER 1 22:11:23 0 7003 chmod 4 CAP_FSETID 1 22:11:23 0 7005 chmod 4 CAP_FSETID 1 22:11:23 0 7005 chmod 4 CAP_FSETID 1 22:11:23 0 7006 chown 4 CAP_FSETID 1 22:11:23 0 7006 chown 4 CAP_FSETID 1 22:11:23 0 6990 setuidgid 6 CAP_SETGID 1 22:11:23 0 6990 setuidgid 6 CAP_SETGID 1 22:11:23 0 6990 setuidgid 7 CAP_SETUID 1 22:11:24 0 7013 run 24 CAP_SYS_RESOURCE 1 22:11:24 0 7026 chmod 3 CAP_FOWNER 1 22:11:24 0 7026 chmod 4 CAP_FSETID 1 22:11:24 0 7028 chmod 4 CAP_FSETID 1 22:11:24 0 7028 chmod 4 CAP_FSETID 1 22:11:24 0 7029 chown 4 CAP_FSETID 1 22:11:24 0 7029 chown 4 CAP_FSETID 1 22:11:24 0 7013 setuidgid 6 CAP_SETGID 1 22:11:24 0 7013 setuidgid 6 CAP_SETGID 1 22:11:24 0 7013 setuidgid 7 CAP_SETUID 1 22:11:25 0 7036 run 24 CAP_SYS_RESOURCE 1 22:11:25 0 7049 chmod 3 CAP_FOWNER 1 22:11:25 0 7049 chmod 4 CAP_FSETID 1 22:11:25 0 7051 chmod 4 CAP_FSETID 1 22:11:25 0 7051 chmod 4 CAP_FSETID 1 [...] This can be useful for general debugging, and also security enforcement: determining a whitelist of capabilities an application needs. The output above includes various capability checks: snmpd checking CAP_NET_ADMIN, run checking CAP_SYS_RESOURCES, then some short-lived processes checking CAP_FOWNER, CAP_FSETID, etc. To see what each of these capabilities does, check the capabilities(7) man page and the kernel source. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.14.0/tools/cpuwalk.bt000077500000000000000000000007611413460502400165250ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * cpuwalk Sample which CPUs are executing processes. * For Linux, uses bpftrace and eBPF. * * USAGE: cpuwalk.bt * * This is a bpftrace version of the DTraceToolkit tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Sampling CPU at 99hz... Hit Ctrl-C to end.\n"); } profile:hz:99 /pid/ { @cpu = lhist(cpu, 0, 1000, 1); } bpftrace-0.14.0/tools/cpuwalk_example.txt000066400000000000000000000114671413460502400204540ustar00rootroot00000000000000Demonstrations of cpuwalk, the Linux bpftrace/eBPF version. cpuwalk samples which CPUs processes are running on, and prints a summary histogram. For example, here is a Linux kernel build on a 36-CPU server: # ./cpuwalk.bt Attaching 2 probes... Sampling CPU at 99hz... Hit Ctrl-C to end. ^C @cpu: [0, 1) 130 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1, 2) 137 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [2, 3) 99 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [3, 4) 99 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 5) 82 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [5, 6) 34 |@@@@@@@@@@@@ | [6, 7) 67 |@@@@@@@@@@@@@@@@@@@@@@@@ | [7, 8) 41 |@@@@@@@@@@@@@@@ | [8, 9) 97 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [9, 10) 140 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [10, 11) 105 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [11, 12) 77 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [12, 13) 39 |@@@@@@@@@@@@@@ | [13, 14) 58 |@@@@@@@@@@@@@@@@@@@@@ | [14, 15) 64 |@@@@@@@@@@@@@@@@@@@@@@@ | [15, 16) 57 |@@@@@@@@@@@@@@@@@@@@@ | [16, 17) 99 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [17, 18) 56 |@@@@@@@@@@@@@@@@@@@@ | [18, 19) 44 |@@@@@@@@@@@@@@@@ | [19, 20) 80 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [20, 21) 64 |@@@@@@@@@@@@@@@@@@@@@@@ | [21, 22) 59 |@@@@@@@@@@@@@@@@@@@@@ | [22, 23) 88 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [23, 24) 84 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [24, 25) 29 |@@@@@@@@@@ | [25, 26) 48 |@@@@@@@@@@@@@@@@@ | [26, 27) 62 |@@@@@@@@@@@@@@@@@@@@@@@ | [27, 28) 66 |@@@@@@@@@@@@@@@@@@@@@@@@ | [28, 29) 57 |@@@@@@@@@@@@@@@@@@@@@ | [29, 30) 59 |@@@@@@@@@@@@@@@@@@@@@ | [30, 31) 56 |@@@@@@@@@@@@@@@@@@@@ | [31, 32) 23 |@@@@@@@@ | [32, 33) 90 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [33, 34) 62 |@@@@@@@@@@@@@@@@@@@@@@@ | [34, 35) 39 |@@@@@@@@@@@@@@ | [35, 36) 68 |@@@@@@@@@@@@@@@@@@@@@@@@@ | This shows that all 36 CPUs were active, with some busier than others. Compare that output to the following workload from an application: # ./cpuwalk.bt Attaching 2 probes... Sampling CPU at 99hz... Hit Ctrl-C to end. ^C @cpu: [6, 7) 243 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [7, 8) 0 | | [8, 9) 0 | | [9, 10) 0 | | [10, 11) 0 | | [11, 12) 0 | | [12, 13) 0 | | [13, 14) 0 | | [14, 15) 0 | | [15, 16) 0 | | [16, 17) 0 | | [17, 18) 0 | | [18, 19) 0 | | [19, 20) 0 | | [20, 21) 1 | | In this case, only a single CPU (6) is really active doing work. Only a single sample was taken of another CPU (20) running a process. If the workload was supposed to be making use of multiple CPUs, it isn't, and that can be investigated (application's configuration, number of threads, CPU binding, etc). bpftrace-0.14.0/tools/dcsnoop.bt000077500000000000000000000023141413460502400165200ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * dcsnoop Trace directory entry cache (dcache) lookups. * For Linux, uses bpftrace and eBPF. * * This uses kernel dynamic tracing of kernel functions, lookup_fast() and * d_lookup(), which will need to be modified to match kernel changes. See * code comments. * * USAGE: dcsnoop.bt * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ #include #include // from fs/namei.c: struct nameidata { struct path path; struct qstr last; // [...] }; BEGIN { printf("Tracing dcache lookups... Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %1s %s\n", "TIME", "PID", "COMM", "T", "FILE"); } // comment out this block to avoid showing hits: kprobe:lookup_fast, kprobe:lookup_fast.constprop.* { $nd = (struct nameidata *)arg0; printf("%-8d %-6d %-16s R %s\n", elapsed / 1e6, pid, comm, str($nd->last.name)); } kprobe:d_lookup { $name = (struct qstr *)arg1; @fname[tid] = $name->name; } kretprobe:d_lookup /@fname[tid]/ { printf("%-8d %-6d %-16s M %s\n", elapsed / 1e6, pid, comm, str(@fname[tid])); delete(@fname[tid]); } bpftrace-0.14.0/tools/dcsnoop_example.txt000066400000000000000000000110041413460502400204360ustar00rootroot00000000000000Demonstrations of dcsnoop, the Linux bpftrace/eBPF version. dcsnoop traces directory entry cache (dcache) lookups, and can be used for further investigation beyond dcstat(8). The output is likely verbose, as dcache lookups are likely frequent. For example: # ./dcsnoop.bt Attaching 4 probes... Tracing dcache lookups... Hit Ctrl-C to end. TIME PID COMM T FILE 427 1518 irqbalance R proc/interrupts 427 1518 irqbalance R interrupts 427 1518 irqbalance R proc/stat 427 1518 irqbalance R stat 483 2440 snmp-pass R proc/cpuinfo 483 2440 snmp-pass R cpuinfo 486 2440 snmp-pass R proc/stat 486 2440 snmp-pass R stat 834 1744 snmpd R proc/net/dev 834 1744 snmpd R net/dev 834 1744 snmpd R self/net 834 1744 snmpd R 1744 834 1744 snmpd R net 834 1744 snmpd R dev 834 1744 snmpd R proc/net/if_inet6 834 1744 snmpd R net/if_inet6 834 1744 snmpd R self/net 834 1744 snmpd R 1744 834 1744 snmpd R net 834 1744 snmpd R if_inet6 835 1744 snmpd R sys/class/net/docker0/device/vendor 835 1744 snmpd R class/net/docker0/device/vendor 835 1744 snmpd R net/docker0/device/vendor 835 1744 snmpd R docker0/device/vendor 835 1744 snmpd R devices/virtual/net/docker0 835 1744 snmpd R virtual/net/docker0 835 1744 snmpd R net/docker0 835 1744 snmpd R docker0 835 1744 snmpd R device/vendor 835 1744 snmpd R proc/sys/net/ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R sys/net/ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R net/ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R neigh/docker0/retrans_time_ms 835 1744 snmpd R docker0/retrans_time_ms 835 1744 snmpd R retrans_time_ms 835 1744 snmpd R proc/sys/net/ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R sys/net/ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R net/ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R neigh/docker0/retrans_time_ms 835 1744 snmpd R docker0/retrans_time_ms 835 1744 snmpd R retrans_time_ms 835 1744 snmpd R proc/sys/net/ipv6/conf/docker0/forwarding 835 1744 snmpd R sys/net/ipv6/conf/docker0/forwarding 835 1744 snmpd R net/ipv6/conf/docker0/forwarding 835 1744 snmpd R ipv6/conf/docker0/forwarding 835 1744 snmpd R conf/docker0/forwarding [...] 5154 934 cksum R usr/bin/basename 5154 934 cksum R bin/basename 5154 934 cksum R basename 5154 934 cksum R usr/bin/bashbug 5154 934 cksum R bin/bashbug 5154 934 cksum R bashbug 5154 934 cksum M bashbug 5155 934 cksum R usr/bin/batch 5155 934 cksum R bin/batch 5155 934 cksum R batch 5155 934 cksum M batch 5155 934 cksum R usr/bin/bc 5155 934 cksum R bin/bc 5155 934 cksum R bc 5155 934 cksum M bc 5169 934 cksum R usr/bin/bdftopcf 5169 934 cksum R bin/bdftopcf 5169 934 cksum R bdftopcf 5169 934 cksum M bdftopcf 5173 934 cksum R usr/bin/bdftruncate 5173 934 cksum R bin/bdftruncate 5173 934 cksum R bdftruncate 5173 934 cksum M bdftruncate The way the dcache is currently implemented, each component of a path is checked in turn. The first line, showing "proc/interrupts" from irqbalance, will be a lookup for "proc" in a directory (that isn't shown here). If it finds "proc", it will then lookup "interrupts" inside net. The script is easily modifiable to only show misses, reducing the volume of the output. Or use the bcc version of this tool, which only shows misses by default: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/execsnoop.bt000077500000000000000000000014071413460502400170600ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * execsnoop.bt Trace new processes via exec() syscalls. * For Linux, uses bpftrace and eBPF. * * This traces when processes call exec(). It is handy for identifying new * processes created via the usual fork()->exec() sequence. Note that the * return value is not currently traced, so the exec() may have failed. * * TODO: switch to tracepoints args. Support more args. Include retval. * * This is a bpftrace version of the bcc tool of the same name. * * 15-Nov-2017 Brendan Gregg Created this. * 11-Sep-2018 " " Switched to use join(). */ BEGIN { printf("%-10s %-5s %s\n", "TIME(ms)", "PID", "ARGS"); } tracepoint:syscalls:sys_enter_exec* { printf("%-10u %-5d ", elapsed / 1e6, pid); join(args->argv); } bpftrace-0.14.0/tools/execsnoop_example.txt000066400000000000000000000032371413460502400210050ustar00rootroot00000000000000Demonstrations of execsnoop, the Linux BPF/bpftrace version. Tracing all new process execution (via exec()): # ./execsnoop.bt Attaching 3 probes... TIME(ms) PID ARGS 2460 3466 ls --color=auto -lh execsnoop.bt execsnoop.bt.0 execsnoop.bt.1 3996 3467 man ls 4005 3473 preconv -e UTF-8 4005 3473 preconv -e UTF-8 4005 3473 preconv -e UTF-8 4005 3473 preconv -e UTF-8 4005 3473 preconv -e UTF-8 4005 3474 tbl 4005 3474 tbl 4005 3474 tbl 4005 3474 tbl 4005 3474 tbl 4005 3476 nroff -mandoc -rLL=193n -rLT=193n -Tutf8 4005 3476 nroff -mandoc -rLL=193n -rLT=193n -Tutf8 4005 3476 nroff -mandoc -rLL=193n -rLT=193n -Tutf8 4005 3476 nroff -mandoc -rLL=193n -rLT=193n -Tutf8 4005 3476 nroff -mandoc -rLL=193n -rLT=193n -Tutf8 4006 3479 pager -rLL=193n 4006 3479 pager -rLL=193n 4006 3479 pager -rLL=193n 4006 3479 pager -rLL=193n 4006 3479 pager -rLL=193n 4007 3481 locale charmap 4008 3482 groff -mtty-char -Tutf8 -mandoc -rLL=193n -rLT=193n 4009 3483 troff -mtty-char -mandoc -rLL=193n -rLT=193n -Tutf8 The output begins by showing an "ls" command, and then the process execution to serve "man ls". The same exec arguments appear multiple times: in this case they are failing as the $PATH variable is walked, until one finally succeeds. This tool can be used to discover unwanted short-lived processes that may be causing performance issues such as latency perturbations. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides more fields and command line options. bpftrace-0.14.0/tools/gethostlatency.bt000077500000000000000000000025501413460502400201120ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * gethostlatency Trace getaddrinfo/gethostbyname[2] calls. * For Linux, uses bpftrace and eBPF. * * This can be useful for identifying DNS latency, by identifying which * remote host name lookups were slow, and by how much. * * This uses dynamic tracing of user-level functions and registers, and may # need modifications to match your software and processor architecture. * * USAGE: gethostlatency.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing getaddr/gethost calls... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %-16s %6s %s\n", "TIME", "PID", "COMM", "LATms", "HOST"); } uprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo, uprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname, uprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname2 { @start[tid] = nsecs; @name[tid] = arg0; } uretprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo, uretprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname, uretprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname2 /@start[tid]/ { $latms = (nsecs - @start[tid]) / 1e6; time("%H:%M:%S "); printf("%-6d %-16s %6d %s\n", pid, comm, $latms, str(@name[tid])); delete(@start[tid]); delete(@name[tid]); } bpftrace-0.14.0/tools/gethostlatency_example.txt000066400000000000000000000016331413460502400220350ustar00rootroot00000000000000Demonstrations of gethostlatency, the Linux bpftrace/eBPF version. This traces host name lookup calls (getaddrinfo(), gethostbyname(), and gethostbyname2()), and shows the PID and command performing the lookup, the latency (duration) of the call in milliseconds, and the host string: # ./gethostlatency.bt Attaching 7 probes... Tracing getaddr/gethost calls... Hit Ctrl-C to end. TIME PID COMM LATms HOST 02:52:05 19105 curl 81 www.netflix.com 02:52:12 19111 curl 17 www.netflix.com 02:52:19 19116 curl 9 www.facebook.com 02:52:23 19118 curl 3 www.facebook.com In this example, the first call to lookup "www.netflix.com" took 81 ms, and the second took 17 ms (sounds like some caching). There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.14.0/tools/killsnoop.bt000077500000000000000000000015431413460502400170700ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * killsnoop Trace signals issued by the kill() syscall. * For Linux, uses bpftrace and eBPF. * * USAGE: killsnoop.bt * * Also a basic example of bpftrace. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 07-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing kill() signals... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %-16s %-4s %-6s %s\n", "TIME", "PID", "COMM", "SIG", "TPID", "RESULT"); } tracepoint:syscalls:sys_enter_kill { @tpid[tid] = args->pid; @tsig[tid] = args->sig; } tracepoint:syscalls:sys_exit_kill /@tpid[tid]/ { time("%H:%M:%S "); printf("%-6d %-16s %-4d %-6d %d\n", pid, comm, @tsig[tid], @tpid[tid], args->ret); delete(@tpid[tid]); delete(@tsig[tid]); } bpftrace-0.14.0/tools/killsnoop_example.txt000066400000000000000000000014451413460502400210130ustar00rootroot00000000000000Demonstrations of killsnoop, the Linux bpftrace/eBPF version. This traces signals sent via the kill() syscall. For example: # ./killsnoop.bt Attaching 3 probes... Tracing kill() signals... Hit Ctrl-C to end. TIME PID COMM SIG TPID RESULT 00:09:37 22485 bash 2 23856 0 00:09:40 22485 bash 2 23856 -3 00:09:31 22485 bash 15 23814 -3 The first line showed a SIGINT (2) sent from PID 22485 (a bash shell) to PID 23856. The result, 0, means success. The next line shows the same signal sent, which resulted in -3, a failure (likely because the target process no longer existed). There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options to customize the output. bpftrace-0.14.0/tools/loads.bt000077500000000000000000000021471413460502400161610ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * loads Prints load averages. * For Linux, uses bpftrace and eBPF. * * These are the same load averages printed by "uptime", but to three decimal * places instead of two (not that it really matters). This is really a * demonstration of fetching and processing a kernel structure from bpftrace. * * USAGE: loads.bt * * This is a bpftrace version of a DTraceToolkit tool. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 10-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Reading load averages... Hit Ctrl-C to end.\n"); } interval:s:1 { /* * See fs/proc/loadavg.c and include/linux/sched/loadavg.h for the * following calculations. */ $avenrun = kaddr("avenrun"); $load1 = *$avenrun; $load5 = *($avenrun + 8); $load15 = *($avenrun + 16); time("%H:%M:%S "); printf("load averages: %d.%03d %d.%03d %d.%03d\n", ($load1 >> 11), (($load1 & ((1 << 11) - 1)) * 1000) >> 11, ($load5 >> 11), (($load5 & ((1 << 11) - 1)) * 1000) >> 11, ($load15 >> 11), (($load15 & ((1 << 11) - 1)) * 1000) >> 11 ); } bpftrace-0.14.0/tools/loads_example.txt000066400000000000000000000015401413460502400200770ustar00rootroot00000000000000Demonstrations of loads, the Linux bpftrace/eBPF version. This is a simple tool that prints the system load averages, to three decimal places each (not that it really matters), as a demonstration of fetching kernel structures from bpftrace: # ./loads.bt Attaching 2 probes... Reading load averages... Hit Ctrl-C to end. 21:29:17 load averages: 2.091 2.048 1.947 21:29:18 load averages: 2.091 2.048 1.947 21:29:19 load averages: 2.091 2.048 1.947 21:29:20 load averages: 2.091 2.048 1.947 21:29:21 load averages: 2.164 2.064 1.953 21:29:22 load averages: 2.164 2.064 1.953 21:29:23 load averages: 2.164 2.064 1.953 ^C These are the same load averages printed by uptime: # uptime 21:29:24 up 2 days, 18:57, 3 users, load average: 2.16, 2.06, 1.95 For more on load averages, see my post: http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html bpftrace-0.14.0/tools/mdflush.bt000077500000000000000000000012251413460502400165150ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * mdflush Trace md flush events. * For Linux, uses bpftrace and eBPF. * * USAGE: mdflush.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ #include #include BEGIN { printf("Tracing md flush events... Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %s", "TIME", "PID", "COMM", "DEVICE"); } kprobe:md_flush_request { time("%H:%M:%S "); printf("%-6d %-16s %s\n", pid, comm, ((struct bio *)arg1)->bi_disk->disk_name); } bpftrace-0.14.0/tools/mdflush_example.txt000066400000000000000000000035121413460502400204400ustar00rootroot00000000000000Demonstrations of mdflush, the Linux bpftrace/eBPF version. The mdflush tool traces flushes at the md driver level, and prints details including the time of the flush: # ./mdflush.bt Tracing md flush requests... Hit Ctrl-C to end. TIME PID COMM DEVICE 03:13:49 16770 sync md0 03:14:08 16864 sync md0 03:14:49 496 kworker/1:0H md0 03:14:49 488 xfsaild/md0 md0 03:14:54 488 xfsaild/md0 md0 03:15:00 488 xfsaild/md0 md0 03:15:02 85 kswapd0 md0 03:15:02 488 xfsaild/md0 md0 03:15:05 488 xfsaild/md0 md0 03:15:08 488 xfsaild/md0 md0 03:15:10 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:12 488 xfsaild/md0 md0 03:15:13 488 xfsaild/md0 md0 03:15:15 488 xfsaild/md0 md0 03:15:19 496 kworker/1:0H md0 03:15:49 496 kworker/1:0H md0 03:15:55 18840 sync md0 03:16:49 496 kworker/1:0H md0 03:17:19 496 kworker/1:0H md0 03:20:19 496 kworker/1:0H md0 03:21:19 496 kworker/1:0H md0 03:21:49 496 kworker/1:0H md0 03:25:19 496 kworker/1:0H md0 [...] This can be useful for correlation with latency outliers or spikes in disk latency, as measured using another tool (eg, system monitoring). If spikes in disk latency often coincide with md flush events, then it would make flushing a target for tuning. Note that the flush events are likely to originate from higher in the I/O stack, such as from file systems. This traces md processing them, and the timestamp corresponds with when md began to issue the flush to disks. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/naptime.bt000077500000000000000000000017561413460502400165210ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * naptime - Show voluntary sleep calls. * * See BPF Performance Tools, Chapter 13, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 16-Feb-2019 Brendan Gregg Created this. */ #include #include BEGIN { printf("Tracing sleeps. Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %-6s %-16s %s\n", "TIME", "PPID", "PCOMM", "PID", "COMM", "SECONDS"); } tracepoint:syscalls:sys_enter_nanosleep /args->rqtp->tv_sec + args->rqtp->tv_nsec/ { $task = (struct task_struct *)curtask; time("%H:%M:%S "); printf("%-6d %-16s %-6d %-16s %d.%03d\n", $task->real_parent->pid, $task->real_parent->comm, pid, comm, args->rqtp->tv_sec, (uint64)args->rqtp->tv_nsec / 1e6); } bpftrace-0.14.0/tools/naptime_example.txt000066400000000000000000000015141413460502400204330ustar00rootroot00000000000000Demonstrations of naptime, the Linux bpftrace/eBPF version. Tracing application sleeps via the nanosleep(2) syscall: # ./naptime.bt Attaching 2 probes... Tracing sleeps. Hit Ctrl-C to end. TIME PCOMM PPID COMM PID SECONDS 15:50:00 1 systemd 1319 mysqld 1.000 15:50:01 4388 bash 25250 sleep 5.000 15:50:01 1 systemd 1319 mysqld 1.000 15:50:01 1 systemd 1180 cron 60.000 15:50:01 1 systemd 1180 cron 60.000 15:50:02 1 systemd 1319 mysqld 1.000 [...] The output shows mysqld performing a one second sleep every second (likely a daemon thread), a sleep(1) command sleeping for five seconds and called by bash, and cron threads sleeping for 60 seconds. bpftrace-0.14.0/tools/oomkill.bt000077500000000000000000000022151413460502400165210ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * oomkill Trace OOM killer. * For Linux, uses bpftrace and eBPF. * * This traces the kernel out-of-memory killer, and prints basic details, * including the system load averages. This can provide more context on the * system state at the time of OOM: was it getting busier or steady, based * on the load averages? This tool may also be useful to customize for * investigations; for example, by adding other task_struct details at the * time of the OOM, or other commands in the system() call. * * This currently works by using kernel dynamic tracing of oom_kill_process(). * * USAGE: oomkill.bt * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 07-Sep-2018 Brendan Gregg Created this. */ #include BEGIN { printf("Tracing oom_kill_process()... Hit Ctrl-C to end.\n"); } kprobe:oom_kill_process { $oc = (struct oom_control *)arg0; time("%H:%M:%S "); printf("Triggered by PID %d (\"%s\"), ", pid, comm); printf("OOM kill of PID %d (\"%s\"), %d pages, loadavg: ", $oc->chosen->pid, $oc->chosen->comm, $oc->totalpages); cat("/proc/loadavg"); } bpftrace-0.14.0/tools/oomkill_example.txt000066400000000000000000000032041413460502400204420ustar00rootroot00000000000000Demonstrations of oomkill, the Linux bpftrace/eBPF version. oomkill is a simple program that traces the Linux out-of-memory (OOM) killer, and shows basic details on one line per OOM kill: # ./oomkill.bt Tracing oom_kill_process()... Ctrl-C to end. 21:03:39 Triggered by PID 3297 ("ntpd"), OOM kill of PID 22516 ("perl"), 3850642 pages, loadavg: 0.99 0.39 0.30 3/282 22724 21:03:48 Triggered by PID 22517 ("perl"), OOM kill of PID 22517 ("perl"), 3850642 pages, loadavg: 0.99 0.41 0.30 2/282 22932 The first line shows that PID 22516, with process name "perl", was OOM killed when it reached 3850642 pages (usually 4 Kbytes per page). This OOM kill happened to be triggered by PID 3297, process name "ntpd", doing some memory allocation. The system log (dmesg) shows pages of details and system context about an OOM kill. What it currently lacks, however, is context on how the system had been changing over time. I've seen OOM kills where I wanted to know if the system was at steady state at the time, or if there had been a recent increase in workload that triggered the OOM event. oomkill provides some context: at the end of the line is the load average information from /proc/loadavg. For both of the oomkills here, we can see that the system was getting busier at the time (a higher 1 minute "average" of 0.99, compared to the 15 minute "average" of 0.30). oomkill can also be the basis of other tools and customizations. For example, you can edit it to include other task_struct details from the target PID at the time of the OOM kill, or to run other commands from the shell. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/opensnoop.bt000077500000000000000000000016711413460502400171000ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * opensnoop Trace open() syscalls. * For Linux, uses bpftrace and eBPF. * * Also a basic example of bpftrace. * * USAGE: opensnoop.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing open syscalls... Hit Ctrl-C to end.\n"); printf("%-6s %-16s %4s %3s %s\n", "PID", "COMM", "FD", "ERR", "PATH"); } tracepoint:syscalls:sys_enter_open, tracepoint:syscalls:sys_enter_openat { @filename[tid] = args->filename; } tracepoint:syscalls:sys_exit_open, tracepoint:syscalls:sys_exit_openat /@filename[tid]/ { $ret = args->ret; $fd = $ret > 0 ? $ret : -1; $errno = $ret > 0 ? 0 : - $ret; printf("%-6d %-16s %4d %3d %s\n", pid, comm, $fd, $errno, str(@filename[tid])); delete(@filename[tid]); } END { clear(@filename); } bpftrace-0.14.0/tools/opensnoop_example.txt000066400000000000000000000047401413460502400210220ustar00rootroot00000000000000Demonstrations of opensnoop, the Linux bpftrace/eBPF version. opensnoop traces the open() syscall system-wide, and prints various details. Example output: # ./opensnoop.bt Attaching 3 probes... Tracing open syscalls... Hit Ctrl-C to end. PID COMM FD ERR PATH 2440 snmp-pass 4 0 /proc/cpuinfo 2440 snmp-pass 4 0 /proc/stat 25706 ls 3 0 /etc/ld.so.cache 25706 ls 3 0 /lib/x86_64-linux-gnu/libselinux.so.1 25706 ls 3 0 /lib/x86_64-linux-gnu/libc.so.6 25706 ls 3 0 /lib/x86_64-linux-gnu/libpcre.so.3 25706 ls 3 0 /lib/x86_64-linux-gnu/libdl.so.2 25706 ls 3 0 /lib/x86_64-linux-gnu/libpthread.so.0 25706 ls 3 0 /proc/filesystems 25706 ls 3 0 /usr/lib/locale/locale-archive 25706 ls 3 0 . 1744 snmpd 8 0 /proc/net/dev 1744 snmpd 21 0 /proc/net/if_inet6 1744 snmpd 21 0 /sys/class/net/eth0/device/vendor 1744 snmpd 21 0 /sys/class/net/eth0/device/device 1744 snmpd 21 0 /proc/sys/net/ipv4/neigh/eth0/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/eth0/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/conf/eth0/forwarding 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/eth0/base_reachable_time_ms 1744 snmpd -1 2 /sys/class/net/lo/device/vendor 1744 snmpd 21 0 /proc/sys/net/ipv4/neigh/lo/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/lo/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/conf/lo/forwarding 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/lo/base_reachable_time_ms 2440 snmp-pass 4 0 /proc/cpuinfo 2440 snmp-pass 4 0 /proc/stat 22884 pickup 12 0 maildrop 2440 snmp-pass 4 0 /proc/cpuinfo 2440 snmp-pass 4 0 /proc/stat While tracing, at "ls" command was launched: the libraries it uses can be seen as they were opened. Also, the snmpd process opened various /proc and /sys files (reading metrics). was starting up: a new process). opensnoop can be useful for discovering configuration and log files, if used during application startup. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options to customize the output. bpftrace-0.14.0/tools/pidpersec.bt000077500000000000000000000011641413460502400170330ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * pidpersec Count new procesess (via fork). * For Linux, uses bpftrace and eBPF. * * Written as a basic example of counting on an event. * * USAGE: pidpersec.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing new processes... Hit Ctrl-C to end.\n"); } tracepoint:sched:sched_process_fork { @ = count(); } interval:s:1 { time("%H:%M:%S PIDs/sec: "); print(@); clear(@); } END { clear(@); } bpftrace-0.14.0/tools/pidpersec_example.txt000066400000000000000000000027401413460502400207560ustar00rootroot00000000000000Demonstrations of pidpersec, the Linux bpftrace/eBPF version. Tracing new procesess: # ./pidpersec.bt Attaching 4 probes... Tracing new processes... Hit Ctrl-C to end. 22:29:50 PIDs/sec: @: 121 22:29:51 PIDs/sec: @: 120 22:29:52 PIDs/sec: @: 122 22:29:53 PIDs/sec: @: 124 22:29:54 PIDs/sec: @: 123 22:29:55 PIDs/sec: @: 121 22:29:56 PIDs/sec: @: 121 22:29:57 PIDs/sec: @: 121 22:29:58 PIDs/sec: @: 49 22:29:59 PIDs/sec: 22:30:00 PIDs/sec: 22:30:01 PIDs/sec: 22:30:02 PIDs/sec: ^C The output begins by showing a rate of new procesess over 120 per second. That then ends at time 22:29:59, and for the next few seconds there are zero new processes per second. The following example shows a Linux build launched at 6:33:40, on a 36 CPU server, with make -j36: # ./pidpersec.bt Attaching 4 probes... Tracing new processes... Hit Ctrl-C to end. 06:33:38 PIDs/sec: 06:33:39 PIDs/sec: 06:33:40 PIDs/sec: @: 2314 06:33:41 PIDs/sec: @: 2517 06:33:42 PIDs/sec: @: 1345 06:33:43 PIDs/sec: @: 1752 06:33:44 PIDs/sec: @: 1744 06:33:45 PIDs/sec: @: 1549 06:33:46 PIDs/sec: @: 1643 06:33:47 PIDs/sec: @: 1487 06:33:48 PIDs/sec: @: 1534 06:33:49 PIDs/sec: @: 1279 06:33:50 PIDs/sec: @: 1392 06:33:51 PIDs/sec: @: 1556 06:33:52 PIDs/sec: @: 1580 06:33:53 PIDs/sec: @: 1944 A Linux kernel build involves launched many thousands of short-lived processes, which can be seen in the above output: a rate of over 1,000 processes per second. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/runqlat.bt000077500000000000000000000014311413460502400165400ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * runqlat.bt CPU scheduler run queue latency as a histogram. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 17-Sep-2018 Brendan Gregg Created this. */ #include BEGIN { printf("Tracing CPU scheduler... Hit Ctrl-C to end.\n"); } tracepoint:sched:sched_wakeup, tracepoint:sched:sched_wakeup_new { @qtime[args->pid] = nsecs; } tracepoint:sched:sched_switch { if (args->prev_state == TASK_RUNNING) { @qtime[args->prev_pid] = nsecs; } $ns = @qtime[args->next_pid]; if ($ns) { @usecs = hist((nsecs - $ns) / 1000); } delete(@qtime[args->next_pid]); } END { clear(@qtime); } bpftrace-0.14.0/tools/runqlat_example.txt000066400000000000000000000206671413460502400204760ustar00rootroot00000000000000Demonstrations of runqlat, the Linux BPF/bpftrace version. This traces time spent waiting in the CPU scheduler for a turn on-CPU. This metric is often called run queue latency, or scheduler latency. This tool shows this latency as a power-of-2 histogram in nanoseconds. For example: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [0] 1 | | [1] 11 |@@ | [2, 4) 16 |@@@ | [4, 8) 43 |@@@@@@@@@@ | [8, 16) 134 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16, 32) 220 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [32, 64) 117 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [64, 128) 84 |@@@@@@@@@@@@@@@@@@@ | [128, 256) 10 |@@ | [256, 512) 2 | | [512, 1K) 5 |@ | [1K, 2K) 5 |@ | [2K, 4K) 5 |@ | [4K, 8K) 4 | | [8K, 16K) 1 | | [16K, 32K) 2 | | [32K, 64K) 0 | | [64K, 128K) 1 | | [128K, 256K) 0 | | [256K, 512K) 0 | | [512K, 1M) 1 | | This is an idle system where most of the time we are waiting for less than 128 microseconds, shown by the mode above. As an example of reading the output, the above histogram shows 220 scheduling events with a run queue latency of between 16 and 32 microseconds. The output also shows an outlier taking between 0.5 and 1 seconds: ??? XXX likely work was scheduled behind another higher priority task, and had to wait briefly. The kernel decides whether it is worth migrating such work to an idle CPU, or leaving it wait its turn on its current CPU run queue where the CPU caches should be hotter. I'll now add a single-threaded CPU bound workload to this system, and bind it on one CPU: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [1] 6 |@@@ | [2, 4) 26 |@@@@@@@@@@@@@ | [4, 8) 97 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8, 16) 72 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16, 32) 17 |@@@@@@@@@ | [32, 64) 19 |@@@@@@@@@@ | [64, 128) 20 |@@@@@@@@@@ | [128, 256) 3 |@ | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 1 | | [2K, 4K) 1 | | [4K, 8K) 4 |@@ | [8K, 16K) 3 |@ | [16K, 32K) 0 | | [32K, 64K) 0 | | [64K, 128K) 0 | | [128K, 256K) 1 | | [256K, 512K) 0 | | [512K, 1M) 0 | | [1M, 2M) 1 | | That didn't make much difference. Now I'll add a second single-threaded CPU workload, and bind it to the same CPU, causing contention: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [0] 1 | | [1] 8 |@@@ | [2, 4) 28 |@@@@@@@@@@@@ | [4, 8) 95 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [8, 16) 120 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [16, 32) 22 |@@@@@@@@@ | [32, 64) 10 |@@@@ | [64, 128) 7 |@@@ | [128, 256) 3 |@ | [256, 512) 1 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 2 | | [4K, 8K) 4 |@ | [8K, 16K) 107 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16K, 32K) 0 | | [32K, 64K) 0 | | [64K, 128K) 0 | | [128K, 256K) 0 | | [256K, 512K) 1 | | There's now a second mode between 8 and 16 milliseconds, as each thread must wait its turn on the one CPU. Now I'l run 10 CPU-bound throuds on one CPU: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [0] 2 | | [1] 10 |@ | [2, 4) 38 |@@@@ | [4, 8) 63 |@@@@@@ | [8, 16) 106 |@@@@@@@@@@@ | [16, 32) 28 |@@@ | [32, 64) 13 |@ | [64, 128) 15 |@ | [128, 256) 2 | | [256, 512) 2 | | [512, 1K) 1 | | [1K, 2K) 1 | | [2K, 4K) 2 | | [4K, 8K) 4 | | [8K, 16K) 3 | | [16K, 32K) 0 | | [32K, 64K) 478 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64K, 128K) 1 | | [128K, 256K) 0 | | [256K, 512K) 0 | | [512K, 1M) 0 | | [1M, 2M) 1 | | This shows that most of the time threads need to wait their turn, with the largest mode between 32 and 64 milliseconds. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.14.0/tools/runqlen.bt000077500000000000000000000020341413460502400165360ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * runqlen.bt CPU scheduler run queue length as a histogram. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 07-Oct-2018 Brendan Gregg Created this. */ #include // Until BTF is available, we'll need to declare some of this struct manually, // since it isn't available to be #included. This will need maintenance to match // your kernel version. It is from kernel/sched/sched.h: struct cfs_rq_partial { struct load_weight load; unsigned long runnable_weight; unsigned int nr_running; unsigned int h_nr_running; }; BEGIN { printf("Sampling run queue length at 99 Hertz... Hit Ctrl-C to end.\n"); } profile:hz:99 { $task = (struct task_struct *)curtask; $my_q = (struct cfs_rq_partial *)$task->se.cfs_rq; $len = $my_q->nr_running; $len = $len > 0 ? $len - 1 : 0; // subtract currently running task @runqlen = lhist($len, 0, 100, 1); } bpftrace-0.14.0/tools/runqlen_example.txt000066400000000000000000000017241413460502400204650ustar00rootroot00000000000000Demonstrations of runqlen, the Linux BPF/bpftrace version. This tool samples the length of the CPU scheduler run queues, showing these sampled lengths as a histogram. This can be used to characterize demand for CPU resources. For example: # ./runqlen.bt Attaching 2 probes... Sampling run queue length at 99 Hertz... Hit Ctrl-C to end. ^C @runqlen: [0, 1) 1967 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1, 2) 0 | | [2, 3) 0 | | [3, 4) 306 |@@@@@@@@ | This output shows that the run queue length was usually zero, except for some samples where it was 3. This was caused by binding 4 CPU bound threads to a single CPUs. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.14.0/tools/setuids.bt000077500000000000000000000034161413460502400165370ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * setuids - Trace the setuid syscalls: privilege escalation. * * See BPF Performance Tools, Chapter 11, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 26-Feb-2019 Brendan Gregg Created this. */ BEGIN { printf("Tracing setuid(2) family syscalls. Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %-6s %-9s %s\n", "TIME", "PID", "COMM", "UID", "SYSCALL", "ARGS (RET)"); } tracepoint:syscalls:sys_enter_setuid, tracepoint:syscalls:sys_enter_setfsuid { @uid[tid] = uid; @setuid[tid] = args->uid; @seen[tid] = 1; } tracepoint:syscalls:sys_enter_setresuid { @uid[tid] = uid; @ruid[tid] = args->ruid; @euid[tid] = args->euid; @suid[tid] = args->suid; @seen[tid] = 1; } tracepoint:syscalls:sys_exit_setuid /@seen[tid]/ { time("%H:%M:%S "); printf("%-6d %-16s %-6d setuid uid=%d (%d)\n", pid, comm, @uid[tid], @setuid[tid], args->ret); delete(@seen[tid]); delete(@uid[tid]); delete(@setuid[tid]); } tracepoint:syscalls:sys_exit_setfsuid /@seen[tid]/ { time("%H:%M:%S "); printf("%-6d %-16s %-6d setfsuid uid=%d (prevuid=%d)\n", pid, comm, @uid[tid], @setuid[tid], args->ret); delete(@seen[tid]); delete(@uid[tid]); delete(@setuid[tid]); } tracepoint:syscalls:sys_exit_setresuid /@seen[tid]/ { time("%H:%M:%S "); printf("%-6d %-16s %-6d setresuid ", pid, comm, @uid[tid]); printf("ruid=%d euid=%d suid=%d (%d)\n", @ruid[tid], @euid[tid], @suid[tid], args->ret); delete(@seen[tid]); delete(@uid[tid]); delete(@ruid[tid]); delete(@euid[tid]); delete(@suid[tid]); } bpftrace-0.14.0/tools/setuids_example.txt000066400000000000000000000046111413460502400204570ustar00rootroot00000000000000Demonstrations of setuids, the Linux bpftrace/eBPF version. This tool traces privilege escalation via setuid syscalls (setuid(2), setfsuid(2), retresuid(2)). For example, here are the setuid calls during an ssh login: # ./setuids.bt Attaching 7 probes... Tracing setuid(2) family syscalls. Hit Ctrl-C to end. TIME PID COMM UID SYSCALL ARGS (RET) 14:28:22 21785 ssh 1000 setresuid ruid=-1 euid=1000 suid=-1 (0) 14:28:22 21787 sshd 0 setresuid ruid=122 euid=122 suid=122 (0) 14:28:22 21787 sshd 122 setuid uid=0 (-1) 14:28:22 21787 sshd 122 setresuid ruid=-1 euid=0 suid=-1 (-1) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=1000 suid=-1 (0) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=0 suid=-1 (0) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=1000 suid=-1 (0) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=0 suid=-1 (0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=0) 14:28:24 21851 sshd 0 setresuid ruid=1000 euid=1000 suid=1000 (0) 14:28:24 21851 sshd 1000 setuid uid=0 (-1) 14:28:24 21851 sshd 1000 setresuid ruid=-1 euid=0 suid=-1 (-1) Why does sshd make so many calls? I don't know! Nevertheless, this shows what this tool can do: it shows the caller details (PID, COMM, and UID), the syscall (SYSCALL), and the syscall arguments (ARGS) and return value (RET). You can modify this tool to print user stack traces for each call, which will show the code path in sshd (provided it is compiled with frame pointers). bpftrace-0.14.0/tools/statsnoop.bt000077500000000000000000000024221413460502400171050ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * statsnoop Trace stat() syscalls. * For Linux, uses bpftrace and eBPF. * * This traces the traecepoints for statfs(), statx(), newstat(), and * newlstat(). These aren't the only the stat syscalls: if you are missing * activity, you may need to add more variants. * * Also a basic example of bpftrace. * * USAGE: statsnoop.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing stat syscalls... Hit Ctrl-C to end.\n"); printf("%-6s %-16s %3s %s\n", "PID", "COMM", "ERR", "PATH"); } tracepoint:syscalls:sys_enter_statfs { @filename[tid] = args->pathname; } tracepoint:syscalls:sys_enter_statx, tracepoint:syscalls:sys_enter_newstat, tracepoint:syscalls:sys_enter_newlstat { @filename[tid] = args->filename; } tracepoint:syscalls:sys_exit_statfs, tracepoint:syscalls:sys_exit_statx, tracepoint:syscalls:sys_exit_newstat, tracepoint:syscalls:sys_exit_newlstat /@filename[tid]/ { $ret = args->ret; $errno = $ret >= 0 ? 0 : - $ret; printf("%-6d %-16s %3d %s\n", pid, comm, $errno, str(@filename[tid])); delete(@filename[tid]); } END { clear(@filename); } bpftrace-0.14.0/tools/statsnoop_example.txt000066400000000000000000000052621413460502400210340ustar00rootroot00000000000000Demonstrations of statsnoop, the Linux bpftrace/eBPF version. statsnoop traces different stat() syscalls system-wide, and prints details. Example output: # ./statsnoop.bt Attaching 9 probes... Tracing stat syscalls... Hit Ctrl-C to end. PID COMM ERR PATH 27835 bash 0 . 27835 bash 2 /usr/local/sbin/iconfig 27835 bash 2 /usr/local/bin/iconfig 27835 bash 2 /usr/sbin/iconfig 27835 bash 2 /usr/bin/iconfig 27835 bash 2 /sbin/iconfig 27835 bash 2 /bin/iconfig 27835 bash 2 /usr/games/iconfig 27835 bash 2 /usr/local/games/iconfig 27835 bash 2 /snap/bin/iconfig 27835 bash 2 /apps/python/bin/iconfig 30573 command-not-fou 2 /usr/bin/Modules/Setup 30573 command-not-fou 2 /usr/bin/lib/python3.5/os.py 30573 command-not-fou 2 /usr/bin/lib/python3.5/os.pyc 30573 command-not-fou 0 /usr/lib/python3.5/os.py 30573 command-not-fou 2 /usr/bin/pybuilddir.txt 30573 command-not-fou 2 /usr/bin/lib/python3.5/lib-dynload 30573 command-not-fou 0 /usr/lib/python3.5/lib-dynload 30573 command-not-fou 2 /usr/lib/python35.zip 30573 command-not-fou 0 /usr/lib 30573 command-not-fou 2 /usr/lib/python35.zip 30573 command-not-fou 0 /usr/lib/python3.5/ 30573 command-not-fou 0 /usr/lib/python3.5/ 30573 command-not-fou 0 /usr/lib/python3.5/ 30573 command-not-fou 2 /usr/lib/python3.5/encodings/__init__.cpython-35m-x86_64-linux- 30573 command-not-fou 2 /usr/lib/python3.5/encodings/__init__.abi3.so 30573 command-not-fou 2 /usr/lib/python3.5/encodings/__init__.so 30573 command-not-fou 0 /usr/lib/python3.5/encodings/__init__.py 30573 command-not-fou 0 /usr/lib/python3.5/encodings/__init__.py This output has caught me mistyping a command in another shell, "iconfig" instead of "ifconfig". The first several lines show the bash shell searching the $PATH (why is games in my $PATH??), and failing to find it (ERR == 2 is file not found). Then, a "command-not-found" program executes (the name is truncated to 16 characters in the COMM field, including the NULL), which begins the process of searching for and suggesting a package. ie, this: # iconfig The program 'iconfig' is currently not installed. You can install it by typing: apt install ipmiutil statsnoop can be used for general debugging, to see what file information has been requested, and whether those files exist. It can be used as a companion to opensnoop, which shows what files were actually opened. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.14.0/tools/swapin.bt000077500000000000000000000011301413460502400163470ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * swapin - Show swapins by process. * * See BPF Performance Tools, Chapter 7, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 26-Jan-2019 Brendan Gregg Created this. */ kprobe:swap_readpage { @[comm, pid] = count(); } interval:s:1 { time(); print(@); clear(@); } bpftrace-0.14.0/tools/swapin_example.txt000066400000000000000000000010451413460502400202760ustar00rootroot00000000000000Demonstrations of swapin, the Linux BCC/eBPF version. This tool counts swapins by process, to show which process is affected by swapping. For example: # ./swapin.bt Attaching 2 probes... 13:36:59 13:37:00 @[chrome, 4536]: 10809 @[gnome-shell, 2239]: 12410 13:37:01 @[chrome, 4536]: 3826 13:37:02 @[cron, 1180]: 23 @[gnome-shell, 2239]: 2462 13:37:03 @[gnome-shell, 1444]: 4 @[gnome-shell, 2239]: 3420 13:37:04 13:37:05 [...] While tracing, this showed that PID 2239 (gnome-shell) and PID 4536 (chrome) suffered over ten thousand swapins. bpftrace-0.14.0/tools/syncsnoop.bt000077500000000000000000000015071413460502400171110ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * syncsnoop Trace sync() variety of syscalls. * For Linux, uses bpftrace and eBPF. * * Also a basic example of bpftrace. * * USAGE: syncsnoop.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing sync syscalls... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %-16s %s\n", "TIME", "PID", "COMM", "EVENT"); } tracepoint:syscalls:sys_enter_sync, tracepoint:syscalls:sys_enter_syncfs, tracepoint:syscalls:sys_enter_fsync, tracepoint:syscalls:sys_enter_fdatasync, tracepoint:syscalls:sys_enter_sync_file_range*, tracepoint:syscalls:sys_enter_msync { time("%H:%M:%S "); printf("%-6d %-16s %s\n", pid, comm, probe); } bpftrace-0.14.0/tools/syncsnoop_example.txt000066400000000000000000000010351413460502400210270ustar00rootroot00000000000000Demonstrations of syncsnoop, the Linux bpftrace/eBPF version. Tracing file system sync events: # ./syncsnoop.bt Attaching 7 probes... Tracing sync syscalls... Hit Ctrl-C to end. TIME PID COMM EVENT 02:02:17 27933 sync tracepoint:syscalls:sys_enter_sync 02:03:43 27936 sync tracepoint:syscalls:sys_enter_sync The output shows calls to the sync() syscall (traced via its tracepoint), along with various details. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/syscount.bt000077500000000000000000000015511413460502400167440ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * syscount.bt Count system calls. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * The bcc versions translates syscall IDs to their names, and this version * currently does not. Syscall IDs can be listed by "ausyscall --dump". * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 13-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Counting syscalls... Hit Ctrl-C to end.\n"); // ausyscall --dump | awk 'NR > 1 { printf("\t@sysname[%d] = \"%s\";\n", $1, $2); }' } tracepoint:raw_syscalls:sys_enter { @syscall[args->id] = count(); @process[comm] = count(); } END { printf("\nTop 10 syscalls IDs:\n"); print(@syscall, 10); clear(@syscall); printf("\nTop 10 processes:\n"); print(@process, 10); clear(@process); } bpftrace-0.14.0/tools/syscount_example.txt000066400000000000000000000021701413460502400206640ustar00rootroot00000000000000Demonstrations of syscount, the Linux bpftrace/eBPF version. syscount counts system calls, and prints summaries of the top ten syscall IDs, and the top ten process names making syscalls. For example: # ./syscount.bt Attaching 3 probes... Counting syscalls... Hit Ctrl-C to end. ^C Top 10 syscalls IDs: @syscall[6]: 36862 @syscall[21]: 42189 @syscall[13]: 44532 @syscall[12]: 58456 @syscall[9]: 82113 @syscall[8]: 95575 @syscall[5]: 147658 @syscall[3]: 163269 @syscall[2]: 270801 @syscall[4]: 326333 Top 10 processes: @process[rm]: 14360 @process[tail]: 16011 @process[objtool]: 20767 @process[fixdep]: 28489 @process[as]: 48982 @process[gcc]: 90652 @process[command-not-fou]: 172874 @process[sh]: 270515 @process[cc1]: 482888 @process[make]: 1404065 The above output was traced during a Linux kernel build, and the process name with the most syscalls was "make" with 1,404,065 syscalls while tracing. The highest syscall ID was 4, which is stat(). There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides different command line options, and translates the syscall IDs to their syscall names. bpftrace-0.14.0/tools/tcpaccept.bt000077500000000000000000000032721413460502400170250ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpaccept.bt Trace TCP accept()s * For Linux, uses bpftrace and eBPF. * * USAGE: tcpaccept.bt * * This is a bpftrace version of the bcc tool of the same name. * * This uses dynamic tracing of the kernel inet_csk_accept() socket function * (from tcp_prot.accept), and will need to be modified to match kernel changes. * Copyright (c) 2018 Dale Hamel. * Licensed under the Apache License, Version 2.0 (the "License") * 23-Nov-2018 Dale Hamel created this. */ #include #include BEGIN { printf("Tracing TCP accepts. Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-14s ", "TIME", "PID", "COMM"); printf("%-39s %-5s %-39s %-5s %s\n", "RADDR", "RPORT", "LADDR", "LPORT", "BL"); } kretprobe:inet_csk_accept { $sk = (struct sock *)retval; $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { // initialize variable type: $daddr = ntop(0); $saddr = ntop(0); if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop( $sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop( $sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; $qlen = $sk->sk_ack_backlog; $qmax = $sk->sk_max_ack_backlog; // Destination port is big endian, it must be flipped $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); time("%H:%M:%S "); printf("%-6d %-14s ", pid, comm); printf("%-39s %-5d %-39s %-5d ", $daddr, $dport, $saddr, $lport); printf("%d/%d\n", $qlen, $qmax); } } bpftrace-0.14.0/tools/tcpaccept_example.txt000066400000000000000000000025061413460502400207460ustar00rootroot00000000000000Demonstrations of tcpaccept, the Linux bpftrace/eBPF version. This tool traces the kernel function accepting TCP socket connections (eg, a passive connection via accept(); not connect()). Some example output (IP addresses changed to protect the innocent): # ./tcpaccept.bt Tracing tcp accepts. Hit Ctrl-C to end. TIME PID COMM RADDR RPORT LADDR LPORT BL 00:34:19 3949061 nginx 10.228.22.228 44226 10.229.20.169 8088 0/128 00:34:19 3951399 ruby 127.0.0.1 52422 127.0.0.1 8000 0/128 00:34:19 3949062 nginx 10.228.23.128 35408 10.229.20.169 8080 0/128 This output shows three connections, an IPv4 connections to PID 3951399, a "ruby" process listening on port 8000, and one connection to a "nginx" process listening on port 8080. The remote address and port are also printed, and the accept queue current size as well as maximum size are shown. The overhead of this tool should be negligible, since it is only tracing the kernel function performing accept. It is not tracing every packet and then filtering. This tool only traces successful TCP accept()s. Connection attempts to closed ports will not be shown (those can be traced via other functions). There is another version of this tool in bcc: https://github.com/iovisor/bcc USAGE message: # ./tcpaccept.bt bpftrace-0.14.0/tools/tcpconnect.bt000077500000000000000000000031211413460502400172100ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpconnect.bt Trace TCP connect()s. * For Linux, uses bpftrace and eBPF. * * USAGE: tcpconnect.bt * * This is a bpftrace version of the bcc tool of the same name. * It is limited to ipv4 addresses. * * All connection attempts are traced, even if they ultimately fail. * * This uses dynamic tracing of kernel functions, and will need to be updated * to match kernel changes. * * Copyright (c) 2018 Dale Hamel. * Licensed under the Apache License, Version 2.0 (the "License") * * 23-Nov-2018 Dale Hamel created this. */ #include #include BEGIN { printf("Tracing tcp connections. Hit Ctrl-C to end.\n"); printf("%-8s %-8s %-16s ", "TIME", "PID", "COMM"); printf("%-39s %-6s %-39s %-6s\n", "SADDR", "SPORT", "DADDR", "DPORT"); } kprobe:tcp_connect { $sk = ((struct sock *) arg0); $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop($sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop($sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); time("%H:%M:%S "); printf("%-8d %-16s ", pid, comm); printf("%-39s %-6d %-39s %-6d\n", $saddr, $lport, $daddr, $dport); } } bpftrace-0.14.0/tools/tcpconnect_example.txt000066400000000000000000000020751413460502400211410ustar00rootroot00000000000000Demonstrations of tcpconnect, the Linux bpftrace/eBPF version. This tool traces the kernel function performing active TCP connections (eg, via a connect() syscall; accept() are passive connections). Some example output (IP addresses changed to protect the innocent): # ./tcpconnect.bt TIME PID COMM SADDR SPORT DADDR DPORT 00:36:45 1798396 agent 127.0.0.1 5001 10.229.20.82 56114 00:36:45 1798396 curl 127.0.0.1 10255 10.229.20.82 56606 00:36:45 3949059 nginx 127.0.0.1 8000 127.0.0.1 37780 This output shows three connections, one from a "agent" process, one from "curl", and one from "nginx". The output details shows the IP version, source address, source socket port, destination address, and destination port. This traces attempted connections: these may have failed. The overhead of this tool should be negligible, since it is only tracing the kernel functions performing connect. It is not tracing every packet and then filtering. USAGE message: # ./tcpconnect.bt bpftrace-0.14.0/tools/tcpdrop.bt000077500000000000000000000042461413460502400165340ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpdrop.bt Trace TCP kernel-dropped packets/segments. * For Linux, uses bpftrace and eBPF. * * USAGE: tcpdrop.bt * * This is a bpftrace version of the bcc tool of the same name. * It is limited to ipv4 addresses, and cannot show tcp flags. * * This provides information such as packet details, socket state, and kernel * stack trace for packets/segments that were dropped via tcp_drop(). * Copyright (c) 2018 Dale Hamel. * Licensed under the Apache License, Version 2.0 (the "License") * 23-Nov-2018 Dale Hamel created this. */ #include #include BEGIN { printf("Tracing tcp drops. Hit Ctrl-C to end.\n"); printf("%-8s %-8s %-16s %-21s %-21s %-8s\n", "TIME", "PID", "COMM", "SADDR:SPORT", "DADDR:DPORT", "STATE"); // See https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h @tcp_states[1] = "ESTABLISHED"; @tcp_states[2] = "SYN_SENT"; @tcp_states[3] = "SYN_RECV"; @tcp_states[4] = "FIN_WAIT1"; @tcp_states[5] = "FIN_WAIT2"; @tcp_states[6] = "TIME_WAIT"; @tcp_states[7] = "CLOSE"; @tcp_states[8] = "CLOSE_WAIT"; @tcp_states[9] = "LAST_ACK"; @tcp_states[10] = "LISTEN"; @tcp_states[11] = "CLOSING"; @tcp_states[12] = "NEW_SYN_RECV"; } kprobe:tcp_drop { $sk = ((struct sock *) arg0); $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop($sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop($sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); $state = $sk->__sk_common.skc_state; $statestr = @tcp_states[$state]; time("%H:%M:%S "); printf("%-8d %-16s ", pid, comm); printf("%39s:%-6d %39s:%-6d %-10s\n", $saddr, $lport, $daddr, $dport, $statestr); printf("%s\n", kstack); } } END { clear(@tcp_states); } bpftrace-0.14.0/tools/tcpdrop_example.txt000066400000000000000000000023501413460502400204500ustar00rootroot00000000000000Demonstrations of tcpdrop, the Linux bpftrace/eBPF version. tcpdrop prints details of TCP packets or segments that were dropped by the kernel, including the kernel stack trace that led to the drop: # ./tcpdrop.bt TIME PID COMM SADDR:SPORT DADDR:DPORT STATE 00:39:21 0 swapper/2 10.231.244.31:3306 10.229.20.82:50552 ESTABLISHE tcp_drop+0x1 tcp_v4_do_rcv+0x135 tcp_v4_rcv+0x9c7 ip_local_deliver_finish+0x62 ip_local_deliver+0x6f ip_rcv_finish+0x129 ip_rcv+0x28f __netif_receive_skb_core+0x432 __netif_receive_skb+0x18 netif_receive_skb_internal+0x37 napi_gro_receive+0xc5 ena_clean_rx_irq+0x3c3 ena_io_poll+0x33f net_rx_action+0x140 __softirqentry_text_start+0xdf irq_exit+0xb6 do_IRQ+0x82 ret_from_intr+0x0 native_safe_halt+0x6 default_idle+0x20 arch_cpu_idle+0x15 default_idle_call+0x23 do_idle+0x17f cpu_startup_entry+0x73 rest_init+0xae start_kernel+0x4dc x86_64_start_reservations+0x24 x86_64_start_kernel+0x74 secondary_startup_64+0xa5 [...] The last column shows the state of the TCP session. This tool is useful for debugging high rates of drops, which can cause the remote end to do timer-based retransmits, hurting performance. USAGE: # ./tcpdrop.bt bpftrace-0.14.0/tools/tcplife.bt000077500000000000000000000052751413460502400165120ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcplife - Trace TCP session lifespans with connection details. * * See BPF Performance Tools, Chapter 10, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 17-Apr-2019 Brendan Gregg Created this. */ #include #include #include #include BEGIN { printf("%-5s %-10s %-15s %-5s %-15s %-5s ", "PID", "COMM", "LADDR", "LPORT", "RADDR", "RPORT"); printf("%5s %5s %s\n", "TX_KB", "RX_KB", "MS"); } kprobe:tcp_set_state { $sk = (struct sock *)arg0; $newstate = arg1; /* * This tool includes PID and comm context. From TCP this is best * effort, and may be wrong in some situations. It does this: * - record timestamp on any state < TCP_FIN_WAIT1 * note some state transitions may not be present via this kprobe * - cache task context on: * TCP_SYN_SENT: tracing from client * TCP_LAST_ACK: client-closed from server * - do output on TCP_CLOSE: * fetch task context if cached, or use current task */ // record first timestamp seen for this socket if ($newstate < TCP_FIN_WAIT1 && @birth[$sk] == 0) { @birth[$sk] = nsecs; } // record PID & comm on SYN_SENT if ($newstate == TCP_SYN_SENT || $newstate == TCP_LAST_ACK) { @skpid[$sk] = pid; @skcomm[$sk] = comm; } // session ended: calculate lifespan and print if ($newstate == TCP_CLOSE && @birth[$sk]) { $delta_ms = (nsecs - @birth[$sk]) / 1e6; $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; $dport = ($dport >> 8) | (($dport << 8) & 0xff00); $tp = (struct tcp_sock *)$sk; $pid = @skpid[$sk]; $comm = @skcomm[$sk]; if ($comm == "") { // not cached, use current task $pid = pid; $comm = comm; } $family = $sk->__sk_common.skc_family; $saddr = ntop(0); $daddr = ntop(0); if ($family == AF_INET) { $saddr = ntop(AF_INET, $sk->__sk_common.skc_rcv_saddr); $daddr = ntop(AF_INET, $sk->__sk_common.skc_daddr); } else { // AF_INET6 $saddr = ntop(AF_INET6, $sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); $daddr = ntop(AF_INET6, $sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); } printf("%-5d %-10.10s %-15s %-5d %-15s %-6d ", $pid, $comm, $saddr, $lport, $daddr, $dport); printf("%5d %5d %d\n", $tp->bytes_acked / 1024, $tp->bytes_received / 1024, $delta_ms); delete(@birth[$sk]); delete(@skpid[$sk]); delete(@skcomm[$sk]); } } END { clear(@birth); clear(@skpid); clear(@skcomm); } bpftrace-0.14.0/tools/tcplife_example.txt000066400000000000000000000030751413460502400204300ustar00rootroot00000000000000Demonstrations of tcplife, the Linux bpftrace/eBPF version. This tool shows the lifespan of TCP sessions, including througphut statistics, and for efficiency only instruments TCP state changes (rather than all packets). For example: # ./tcplife.bt PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS 20976 ssh 127.0.0.1 56766 127.0.0.1 22 6 10584 3059 20977 sshd 127.0.0.1 22 127.0.0.1 56766 10584 6 3059 14519 monitord 127.0.0.1 44832 127.0.0.1 44444 0 0 0 4496 Chrome_IOT 7f00:6:5ea7::a00:0 42846 0:0:bb01:: 443 0 3 12441 4496 Chrome_IOT 7f00:6:5aa7::a00:0 42842 0:0:bb01:: 443 0 3 12436 4496 Chrome_IOT 7f00:6:62a7::a00:0 42850 0:0:bb01:: 443 0 3 12436 4496 Chrome_IOT 7f00:6:5ca7::a00:0 42844 0:0:bb01:: 443 0 3 12442 4496 Chrome_IOT 7f00:6:60a7::a00:0 42848 0:0:bb01:: 443 0 3 12436 4496 Chrome_IOT 10.0.0.65 33342 54.241.2.241 443 0 3 10717 4496 Chrome_IOT 10.0.0.65 33350 54.241.2.241 443 0 3 10711 4496 Chrome_IOT 10.0.0.65 33352 54.241.2.241 443 0 3 10712 14519 monitord 127.0.0.1 44832 127.0.0.1 44444 0 0 0 The output begins with a localhost ssh connection, so both endpoints can be seen: the ssh process (PID 20976) which received 10584 Kbytes, and the sshd process (PID 20977) which transmitted 10584 Kbytes. This session lasted 3059 milliseconds. Other sessions can also be seen, including IPv6 connections. bpftrace-0.14.0/tools/tcpretrans.bt000077500000000000000000000041101413460502400172340ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpretrans.bt Trace or count TCP retransmits * For Linux, uses bpftrace and eBPF. * * USAGE: tcpretrans.bt * * This is a bpftrace version of the bcc tool of the same name. * It is limited to ipv4 addresses, and doesn't support tracking TLPs. * * This uses dynamic tracing of kernel functions, and will need to be updated * to match kernel changes. * * Copyright (c) 2018 Dale Hamel. * Licensed under the Apache License, Version 2.0 (the "License") * * 23-Nov-2018 Dale Hamel created this. */ #include #include BEGIN { printf("Tracing tcp retransmits. Hit Ctrl-C to end.\n"); printf("%-8s %-8s %20s %21s %6s\n", "TIME", "PID", "LADDR:LPORT", "RADDR:RPORT", "STATE"); // See include/net/tcp_states.h: @tcp_states[1] = "ESTABLISHED"; @tcp_states[2] = "SYN_SENT"; @tcp_states[3] = "SYN_RECV"; @tcp_states[4] = "FIN_WAIT1"; @tcp_states[5] = "FIN_WAIT2"; @tcp_states[6] = "TIME_WAIT"; @tcp_states[7] = "CLOSE"; @tcp_states[8] = "CLOSE_WAIT"; @tcp_states[9] = "LAST_ACK"; @tcp_states[10] = "LISTEN"; @tcp_states[11] = "CLOSING"; @tcp_states[12] = "NEW_SYN_RECV"; } kprobe:tcp_retransmit_skb { $sk = (struct sock *)arg0; $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { // initialize variable type: $daddr = ntop(0); $saddr = ntop(0); if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop( $sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop( $sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); $state = $sk->__sk_common.skc_state; $statestr = @tcp_states[$state]; time("%H:%M:%S "); printf("%-8d %14s:%-6d %14s:%-6d %6s\n", pid, $saddr, $lport, $daddr, $dport, $statestr); } } END { clear(@tcp_states); } bpftrace-0.14.0/tools/tcpretrans_example.txt000066400000000000000000000021721413460502400211640ustar00rootroot00000000000000Demonstrations of tcpretrans, the Linux bpftrace/eBPF version. This tool traces the kernel TCP retransmit function to show details of these retransmits. For example: # ./tcpretrans.bt TIME PID LADDR:LPORT RADDR:RPORT STATE 00:43:54 0 10.229.20.82:46654 153.2.224.76:443 SYN_SENT 00:43:55 0 10.232.0.49:57678 10.229.20.99:24231 SYN_SENT 00:43:57 100 10.229.20.175:54224 10.201.76.122:443 ESTABLISHED [...] This output shows three TCP retransmits, the first two were for an IPv4 connection from 10.153.223.157 port 22 to 69.53.245.40 port 34619. The TCP state was "ESTABLISHED" at the time of the retransmit. The on-CPU PID at the time of the retransmit is printed, in this case 0 (the kernel, which will be the case most of the time). Retransmits are usually a sign of poor network health, and this tool is useful for their investigation. Unlike using tcpdump, this tool has very low overhead, as it only traces the retransmit function. It also prints additional kernel details: the state of the TCP session at the time of the retransmit. USAGE message: # ./tcpretrans.bt bpftrace-0.14.0/tools/tcpsynbl.bt000077500000000000000000000016411413460502400167130ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpsynbl - Show TCP SYN backlog as a histogram. * * See BPF Performance Tools, Chapter 10, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 19-Apr-2019 Brendan Gregg Created this. */ #include BEGIN { printf("Tracing SYN backlog size. Ctrl-C to end.\n"); } kprobe:tcp_v4_syn_recv_sock, kprobe:tcp_v6_syn_recv_sock { $sock = (struct sock *)arg0; @backlog[$sock->sk_max_ack_backlog & 0xffffffff] = hist($sock->sk_ack_backlog); if ($sock->sk_ack_backlog > $sock->sk_max_ack_backlog) { time("%H:%M:%S dropping a SYN.\n"); } } END { printf("\n@backlog[backlog limit]: histogram of backlog size\n"); } bpftrace-0.14.0/tools/tcpsynbl_example.txt000066400000000000000000000016541413460502400206410ustar00rootroot00000000000000Demonstrations of tcpsynbl, the Linux bpftrace/eBPF version. This tool shows the TCP SYN backlog size during SYN arrival as a histogram. This lets you see how close your applications are to hitting the backlog limit and dropping SYNs (causing performance issues with SYN retransmits). For example: # ./tcpsynbl.bt Attaching 4 probes... Tracing SYN backlog size. Ctrl-C to end. ^C @backlog[backlog limit]: histogram of backlog size @backlog[500]: [0] 2266 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1] 3 | | [2, 4) 1 | | This output shows that for the backlog limit of 500, there were 2266 SYN arrivals where the backlog was zero, three where the backlog was one, and one where the backlog was either two or three. This indicates that we are nowhere near this limit. bpftrace-0.14.0/tools/threadsnoop.bt000077500000000000000000000012751413460502400174060ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * threadsnoop - List new thread creation. * * See BPF Performance Tools, Chapter 13, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 15-Feb-2019 Brendan Gregg Created this. */ BEGIN { printf("%-10s %-6s %-16s %s\n", "TIME(ms)", "PID", "COMM", "FUNC"); } uprobe:/lib/x86_64-linux-gnu/libpthread.so.0:pthread_create { printf("%-10u %-6d %-16s %s\n", elapsed / 1e6, pid, comm, usym(arg2)); } bpftrace-0.14.0/tools/threadsnoop_example.txt000066400000000000000000000021121413460502400213170ustar00rootroot00000000000000Demonstrations of threadsnoop, the Linux bpftrace/eBPF version. Tracing new threads via phtread_create(): # ./threadsnoop.bt Attaching 2 probes... TIME(ms) PID COMM FUNC 1938 28549 dockerd threadentry 1939 28549 dockerd threadentry 1939 28549 dockerd threadentry 1940 28549 dockerd threadentry 1949 28549 dockerd threadentry 1958 28549 dockerd threadentry 1939 28549 dockerd threadentry 1950 28549 dockerd threadentry 2013 28579 docker-containe 0x562f30f2e710 2036 28549 dockerd threadentry 2083 28579 docker-containe 0x562f30f2e710 2116 629 systemd-journal 0x7fb7114955c0 2116 629 systemd-journal 0x7fb7114955c0 [...] The output shows a dockerd process creating several threads with the start routine threadentry(), and docker-containe (truncated) and systemd-journal also starting threads: in their cases, the function had no symbol information available, so their addresses are printed in hex. bpftrace-0.14.0/tools/vfscount.bt000077500000000000000000000010031413460502400167140ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * vfscount Count VFS calls ("vfs_*"). * For Linux, uses bpftrace and eBPF. * * Written as a basic example of counting kernel functions. * * USAGE: vfscount.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing VFS calls... Hit Ctrl-C to end.\n"); } kprobe:vfs_* { @[func] = count(); } bpftrace-0.14.0/tools/vfscount_example.txt000066400000000000000000000022601413460502400206440ustar00rootroot00000000000000Demonstrations of vfscount, the Linux bpftrace/eBPF version. Tracing all VFS calls: # ./vfscount.bt Attaching 54 probes... cannot attach kprobe, Invalid argument Warning: could not attach probe kprobe:vfs_dedupe_get_page.isra.21, skipping. Tracing VFS calls... Hit Ctrl-C to end. ^C @[vfs_fsync_range]: 4 @[vfs_readlink]: 14 @[vfs_statfs]: 56 @[vfs_lock_file]: 60 @[vfs_write]: 276 @[vfs_statx]: 328 @[vfs_statx_fd]: 394 @[vfs_open]: 541 @[vfs_getattr]: 595 @[vfs_getattr_nosec]: 597 @[vfs_read]: 1113 While tracing, the vfs_read() call was the most frequent, occurring 1,113 times. VFS is the Virtual File System: a kernel abstraction for file systems and other resources that expose a file system interface. Much of VFS maps directly to the syscall interface. Tracing VFS calls gives you a high level breakdown of the kernel workload, and starting points for further investigation. Notet that a warning was printed: "Warning: could not attach probe kprobe:vfs_dedupe_get_page.isra.21": these are not currently instrumentable by bpftrace/kprobes, so a warning is printed to let you know that they will be missed. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.14.0/tools/vfsstat.bt000077500000000000000000000013211413460502400165420ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * vfsstat Count some VFS calls, with per-second summaries. * For Linux, uses bpftrace and eBPF. * * Written as a basic example of counting multiple events and printing a * per-second summary. * * USAGE: vfsstat.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing key VFS calls... Hit Ctrl-C to end.\n"); } kprobe:vfs_read*, kprobe:vfs_write*, kprobe:vfs_fsync, kprobe:vfs_open, kprobe:vfs_create { @[func] = count(); } interval:s:1 { time(); print(@); clear(@); } END { clear(@); } bpftrace-0.14.0/tools/vfsstat_example.txt000066400000000000000000000016411413460502400204710ustar00rootroot00000000000000Demonstrations of vfsstat, the Linux bpftrace/eBPF version. This traces some common VFS calls (see the script for the list) and prints per-second summaries. # ./vfsstat.bt Attaching 8 probes... Tracing key VFS calls... Hit Ctrl-C to end. 21:30:38 @[vfs_write]: 1274 @[vfs_open]: 8675 @[vfs_read]: 11515 21:30:39 @[vfs_write]: 1155 @[vfs_open]: 8077 @[vfs_read]: 10398 21:30:40 @[vfs_write]: 1222 @[vfs_open]: 8554 @[vfs_read]: 11011 21:30:41 @[vfs_write]: 1230 @[vfs_open]: 8605 @[vfs_read]: 11077 21:30:42 @[vfs_write]: 1229 @[vfs_open]: 8591 @[vfs_read]: 11061 ^C Each second, a timestamp is printed ("HH:MM:SS") followed by common VFS functions and the number of calls for that second. While tracing, the vfs_read() kernel function was most frequent, occurring over 10,000 times per second. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options. bpftrace-0.14.0/tools/writeback.bt000077500000000000000000000032501413460502400170260ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * writeback Trace file system writeback events with details. * For Linux, uses bpftrace and eBPF. * * This traces when file system dirtied pages are flushed to disk by kernel * writeback, and prints details including when the event occurred, and the * duration of the event. This can be useful for correlating these times with * other performance problems, and if there is a match, it would be a clue * that the problem may be caused by writeback. How quickly the kernel does * writeback can be tuned: see the kernel docs, eg, * vm.dirty_writeback_centisecs. * * USAGE: writeback.bt * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 14-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing writeback... Hit Ctrl-C to end.\n"); printf("%-9s %-8s %-8s %-16s %s\n", "TIME", "DEVICE", "PAGES", "REASON", "ms"); // see /sys/kernel/debug/tracing/events/writeback/writeback_start/format @reason[0] = "background"; @reason[1] = "vmscan"; @reason[2] = "sync"; @reason[3] = "periodic"; @reason[4] = "laptop_timer"; @reason[5] = "free_more_memory"; @reason[6] = "fs_free_space"; @reason[7] = "forker_thread"; } tracepoint:writeback:writeback_start { @start[args->sb_dev] = nsecs; } tracepoint:writeback:writeback_written { $sb_dev = args->sb_dev; $s = @start[$sb_dev]; delete(@start[$sb_dev]); $lat = $s ? (nsecs - $s) / 1000 : 0; time("%H:%M:%S "); printf("%-8s %-8d %-16s %d.%03d\n", args->name, args->nr_pages & 0xffff, // TODO: explain these bitmasks @reason[args->reason & 0xffffffff], $lat / 1000, $lat % 1000); } END { clear(@reason); clear(@start); } bpftrace-0.14.0/tools/writeback_example.txt000066400000000000000000000036521413460502400207560ustar00rootroot00000000000000Demonstrations of writeback, the Linux bpftrace/eBPF version. This tool traces when the kernel writeback procedure is writing dirtied pages to disk, and shows details such as the time, device numbers, reason for the write back, and the duration. For example: # ./writeback.bt Attaching 4 probes... Tracing writeback... Hit Ctrl-C to end. TIME DEVICE PAGES REASON ms 23:28:47 259:1 15791 periodic 0.005 23:28:48 259:0 15792 periodic 0.004 23:28:52 259:1 15784 periodic 0.003 23:28:53 259:0 18682 periodic 0.003 23:28:55 259:0 41970 background 326.663 23:28:56 259:0 18418 background 332.689 23:28:56 259:0 60402 background 362.446 23:28:57 259:1 18230 periodic 0.005 23:28:57 259:1 65492 background 3.343 23:28:57 259:1 65492 background 0.002 23:28:58 259:0 36850 background 0.000 23:28:58 259:0 13298 background 597.198 23:28:58 259:0 55282 background 322.050 23:28:59 259:0 31730 background 336.031 23:28:59 259:0 8178 background 357.119 23:29:01 259:0 50162 background 1803.146 23:29:02 259:0 27634 background 1311.876 23:29:03 259:0 6130 background 331.599 23:29:03 259:0 50162 background 293.968 23:29:03 259:0 28658 background 284.946 23:29:03 259:0 7154 background 286.572 [...] By looking a the timestamps and latency, it can be seen that the system was not spending much time in writeback until 23:28:55, when "background" writeback began, taking over 300 milliseconds per flush. If timestamps of heavy writeback coincide with times when applications suffered performance issues, that would be a clue that they are correlated and there is contention for the disk devices. There are various ways to tune this: eg, vm.dirty_writeback_centisecs. bpftrace-0.14.0/tools/xfsdist.bt000077500000000000000000000017141413460502400165420ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * xfsdist Summarize XFS operation latency. * For Linux, uses bpftrace and eBPF. * * This traces four common file system calls: read, write, open, and fsync. * It can be customized to trace more if desired. * * USAGE: xfsdist.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing XFS operation latency... Hit Ctrl-C to end.\n"); } kprobe:xfs_file_read_iter, kprobe:xfs_file_write_iter, kprobe:xfs_file_open, kprobe:xfs_file_fsync { @start[tid] = nsecs; @name[tid] = func; } kretprobe:xfs_file_read_iter, kretprobe:xfs_file_write_iter, kretprobe:xfs_file_open, kretprobe:xfs_file_fsync /@start[tid]/ { @us[@name[tid]] = hist((nsecs - @start[tid]) / 1000); delete(@start[tid]); delete(@name[tid]); } END { clear(@start); clear(@name); } bpftrace-0.14.0/tools/xfsdist_example.txt000066400000000000000000000065331413460502400204700ustar00rootroot00000000000000Demonstrations of xfsdist, the Linux bpftrace/eBPF version. xfsdist traces XFS reads, writes, opens, and fsyncs, and summarizes their latency as a power-of-2 histogram. For example: # xfsdist.bt Attaching 9 probes... Tracing XFS operation latency... Hit Ctrl-C to end. ^C @us[xfs_file_write_iter]: [8, 16) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16, 32) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @us[xfs_file_read_iter]: [1] 724 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [2, 4) 137 |@@@@@@@@@ | [4, 8) 143 |@@@@@@@@@@ | [8, 16) 37 |@@ | [16, 32) 11 | | [32, 64) 22 |@ | [64, 128) 7 | | [128, 256) 0 | | [256, 512) 485 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [512, 1K) 149 |@@@@@@@@@@ | [1K, 2K) 98 |@@@@@@@ | [2K, 4K) 85 |@@@@@@ | [4K, 8K) 27 |@ | [8K, 16K) 29 |@@ | [16K, 32K) 25 |@ | [32K, 64K) 1 | | [64K, 128K) 0 | | [128K, 256K) 6 | | @us[xfs_file_open]: [1] 1819 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [2, 4) 272 |@@@@@@@ | [4, 8) 0 | | [8, 16) 9 | | [16, 32) 7 | | This output shows a bi-modal distribution for read latency, with a faster mode of 724 reads that took between 0 and 1 microseconds, and a slower mode of over 485 reads that took between 256 and 512 microseconds. It's likely that the faster mode was a hit from the in-memory file system cache, and the slower mode is a read from a storage device (disk). This "latency" is measured from when the operation was issued from the VFS interface to the file system, to when it completed. This spans everything: block device I/O (disk I/O), file system CPU cycles, file system locks, run queue latency, etc. This is a better measure of the latency suffered by applications reading from the file system than measuring this down at the block device interface. Note that this only traces the common file system operations previously listed: other file system operations (eg, inode operations including getattr()) are not traced. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options to customize the output.